Secure Ollama with Nginx, HTTPS, and a Password
Stop exposing your raw Ollama port to the internet. Here is exactly how I put Ollama behind Nginx with a real SSL certificate and password protection so I can access my local models safely.

The Problem: "Connection refused" or a naked API
Related: Fix Stable Diffusion Out Of Memory on a 6GB VRAM GPU →
You have a server at home with a decent GPU, and you installed Ollama. By default, it binds to 127.0.0.1:11434. If you try to reach it from your laptop at a coffee shop, you get a connection timeout.
If you just change the environment variable to OLLAMA_HOST=0.0.0.0, the port opens to the entire internet. Ollama has no built-in authentication. Anyone running a port scanner can find your IP, connect to port 11434, and start hammering your GPU with inference requests. I did this accidentally for two days and noticed my GPU fans spinning at 100% while I was asleep.
You need a reverse proxy. You want a domain name (like ai.yourdomain.com), full HTTPS encryption, and a hard password prompt before any traffic touches the Ollama daemon. I run Nginx for this because it handles streaming HTTP responses perfectly. Here is the exact setup running on my rig right now.
Step 1: Bind Ollama tightly to localhost
Related: Fix ComfyUI "Torch Not Compiled With CUDA Enabled" Error →
Before we build the proxy, we have to make sure Ollama is only listening to internal traffic. If you leave Ollama bound to 0.0.0.0, people can just bypass Nginx entirely by hitting port 11434 directly.
If you are running Ollama as a systemd service on Linux, you need to override the default configuration. Do not edit /etc/systemd/system/ollama.service directly, as your changes will be wiped out the next time you update Ollama.
Instead, run this command:
sudo systemctl edit ollamaThis opens a blank override file. Paste in exactly this:
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"Save and exit the editor. Then reload the systemd daemon and restart Ollama:
sudo systemctl daemon-reload
sudo systemctl restart ollamaVerify it is only listening locally by checking your open ports:
sudo ss -tulpn | grep 11434You should see 127.0.0.1:11434 and not *:11434 or 0.0.0.0:11434. Your GPU is now safe from the public internet.
Step 2: Choose your SSL strategy
Related: A One-File Docker Compose Stack for Ollama and Open WebUI →
To get HTTPS, you need an SSL certificate. You cannot pass basic auth credentials over plain HTTP unless you want your password intercepted in plain text by every router between you and your house.
You have three main options for getting traffic to your home server securely.
| Method | Setup time | Cost | Best for |
|---|---|---|---|
| Certbot (Let's Encrypt) | 15 mins | $10/yr (domain) | Accessing your API from normal web apps, Python scripts, and third-party UIs. |
| Tailscale / Wireguard | 5 mins | Free | Private access only. No external web apps can reach it without running the VPN client. |
| Self-signed cert | 2 mins | Free | Local network only. Python and Node scripts will throw ugly SSL validation errors. |
I use Let's Encrypt. I bought a cheap domain, pointed an A record to my home public IP address, and forwarded ports 80 and 443 through my router to my Linux server.
Once your domain is pointing to your server, install Nginx and Certbot:
sudo apt update
sudo apt install nginx certbot python3-certbot-nginxRun Certbot to grab your certificate. Replace the domain with yours:
sudo certbot certonly --nginx -d ai.yourdomain.comI choose certonly because I want to write the Nginx server block manually. Automatic Nginx configurations from Certbot usually strip out the specific proxy flags we need for streaming LLM responses.
Step 3: Generating the basic auth file
Related: Fixing Painfully Slow Whisper Transcription →
Next, we create the file that holds your username and hashed password. Nginx uses this to challenge incoming requests.
You need the Apache utilities package to get the htpasswd command:
sudo apt install apache2-utilsCreate a new password file and add a user. I will use the username ollamauser. The -c flag creates a new file. Do not use -c if you are adding a second user later, or it will overwrite the file.
sudo htpasswd -c /etc/nginx/.htpasswd ollamauserYou will be prompted to type a password twice. Make it long.
Check that the file was created and contains a hashed string:
cat /etc/nginx/.htpasswdYou should see something like ollamauser:$apr1$xyz...
Step 4: Writing the Nginx configuration
Related: Fix Open WebUI Showing No Models in the Dropdown →
This is the most critical part. Ollama does not return a single block of JSON when you prompt it; it streams the response token by token using chunked transfer encoding. If you configure Nginx poorly, it will buffer the entire response and you will sit staring at a blank screen for two minutes before the text suddenly appears all at once.
Create a new Nginx configuration file:
sudo nano /etc/nginx/sites-available/ollamaPaste in the following configuration. Change ai.yourdomain.com to your actual domain.
server {
listen 80;
server_name ai.yourdomain.com;
return 301 https://$host$request_uri;
}server { listen 443 ssl; server_name ai.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/ai.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ai.yourdomain.com/privkey.pem;
Basic Auth setup
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;Allow preflight CORS requests without authentication
# This is vital for web-based frontends
if ($request_method = OPTIONS) {
return 204;
}location / { proxy_pass http://127.0.0.1:11434; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
Disable buffering so token streaming works instantly
proxy_buffering off;
# Massive timeouts because LLMs take a long time to think
proxy_read_timeout 1800s;
proxy_connect_timeout 1800s;
proxy_send_timeout 1800s;Required for streaming chunks
chunked_transfer_encoding on;
}}
A few notes on why this looks the way it does. The proxy_buffering off; line is what allows you to see the text typing out in real-time.
The proxy_read_timeout 1800s; is equally important. If you are reading up on how much RAM you actually need for local AI and pushing your hardware to the absolute limit by offloading to system RAM, your time-to-first-token might be 45 seconds. Nginx defaults to dropping the connection after 60 seconds of silence. If you are experiencing what looks like Ollama running slow and abruptly terminating, it is usually Nginx killing the connection. Setting this to 1800 seconds (30 minutes) gives your GPU all the time it needs.
Enable the site and test the config:
sudo ln -s /etc/nginx/sites-available/ollama /etc/nginx/sites-enabled/
sudo nginx -tIf it says syntax is ok, restart Nginx:
sudo systemctl restart nginxStep 5: Modifying Ollama's environment variables for CORS
Related: How to Stop Ollama From Unloading Models (keep_alive) →
At this point, if you hit https://ai.yourdomain.com from a terminal using curl, passing your password, it works perfectly. But if you try to connect a web-based frontend (like ChatGPT-style web UIs) hosted on another domain, it will fail silently.
If you open the browser console, you will see a nasty CORS error. This happens because the web browser sends an OPTIONS preflight request, and Ollama rejects it because the origin doesn't match. Nginx is letting the OPTIONS request through (because of the if ($request_method = OPTIONS) block we added above), but Ollama itself is blocking it.
We need to tell Ollama to accept requests from anywhere, since Nginx is already acting as our security bouncer.
Open the systemd override file again:
sudo systemctl edit ollamaAdd the OLLAMA_ORIGINS variable to the [Service] block so it looks like this:
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_ORIGINS=*"Reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart ollamaSetting origins to a wildcard * is completely safe here because no request can reach Ollama without first passing the Nginx basic auth check. This exact CORS setup is mandatory if you are researching how MCP servers explained work and want to safely connect external agent tools to your local host.
To test your new secure setup, use a curl command that passes your basic auth credentials natively:
curl -u ollamauser:yourpassword https://ai.yourdomain.com/api/tagsYou should see a JSON array of your installed models. If you get an HTML page complaining about a 401 Unauthorized error, your password file path in the Nginx config is wrong.
What did NOT work
I spent hours fighting this setup before landing on the config above. Here are the dead ends I hit so you don't repeat them:
Handling CORS entirely in Nginx
I initially tried to fix the CORS errors by using Nginx add_header Access-Control-Allow-Origin * directives. This caused a horrific bug where Ollama was also trying to send CORS headers, resulting in duplicated headers. Web browsers strictly refuse requests with duplicate CORS headers. Modifying OLLAMA_ORIGINS and letting Ollama handle the headers itself is the only clean way.
Docker networking assumptions
I originally ran Nginx on the host machine but had my UIs in Docker containers. I spent an entire afternoon debugging why Open WebUI in Docker cannot reach Ollama. I kept pointing my Docker containers to https://ai.yourdomain.com, but my home router did not support NAT loopback (hairpinning). The Docker containers were trying to go out to the public internet and back into my own IP, which my router dropped. I had to add my domain to the Docker container's /etc/hosts file pointing to 172.17.0.1 (the host IP) to fix it.
Using API Keys via custom headers
I tried to set up Nginx to look for a custom Authorization: Bearer <token> header instead of Basic Auth. Nginx requires the commercial "Nginx Plus" or the Lua module to easily validate bearer tokens against a static file. Setting up Lua on an Ubuntu Nginx package was a massive headache. Standard Basic Auth achieves the exact same security with built-in tools.
FAQ
How do I connect the official Python script to this?
You pass the credentials in the client instantiation. Just define the base_url as your new HTTPS domain, and add an auth tuple or an Authorization header containing your base64 encoded username and password.
Can I run Ollama on a different port behind Nginx?
Yes. Just change OLLAMA_HOST=127.0.0.1:8080 in the systemd override file, and then update the proxy_pass http://127.0.0.1:8080; line in your Nginx configuration.
Does basic auth break local CLI usage on the server?
No. If you SSH into the server and type ollama run llama3, the CLI talks directly to the local port 11434. It bypasses Nginx completely, so it does not ask you for a password.
Why do I get a 502 Bad Gateway error?
Ollama is offline or Nginx has the wrong proxy port. Ensure you ran sudo systemctl restart ollama. Run curl http://127.0.0.1:11434 locally on the server; if that fails, Ollama is dead.
مواضيع مقترحة · Suggested Topics
استكشف مواضيع ومحاور ذات صلة بهذا المقال — روابط داخلية لتعميق قراءتك.
The Daily Pulse
Newsletter delivery is not connected yet. This form only saves your address in this browser; no email is sent.
Get concise, source-linked technology notes without the hype.