Expose the Ollama API Safely with an API Key
Keep Ollama on localhost and put Caddy in front with an API key and HTTPS, so OpenAI-compatible clients can reach it from other machines and nobody else can.
Ollama's API has no authentication. Its docs say plainly that the local API at http://localhost:11434 does not require it. That is fine while Ollama listens only on 127.0.0.1, its default. Set OLLAMA_HOST=0.0.0.0 to reach it from elsewhere, though, and anyone who finds the port can run your models, pull new ones and delete the ones you have.
This guide keeps Ollama on loopback and puts Caddy in front of it, with HTTPS and an API key checked on every request. OpenAI-compatible clients already send a key: Ollama's docs say the client "requires an API key value, but Ollama ignores it". With the proxy, that key starts to count.
The Ollama self-host guide protects the API with a username and password (basic_auth). A bearer key suits API clients and SDKs better, because they send one without changes. Prefer one click? Deploy Ollama from the VeloxaHost marketplace.
What you need
- Ollama installed as in the self-host guide, listening on its default
127.0.0.1:11434. - A domain or subdomain, such as
ollama.example.com. - A dedicated IPv4 address. Caddy needs ports 80 and 443 to get a certificate and serve HTTPS. The shared IPv4 address every server gets has no 80 or 443, only a block of forwarded ports. A dedicated IPv4 is a paid add-on (the console shows the current price): choose it as "Network access" on the create page, or take one from the IP addresses page and attach it.
The proxy adds little load; size the server for the model, as in choosing a model size.
Keep Ollama on loopback
Check what Ollama listens on (ours):
ss -ltnp | grep 11434
The line should show 127.0.0.1:11434. If it shows 0.0.0.0 or *, an OLLAMA_HOST line is set: open the override with sudo systemctl edit ollama.service, delete it, then reload and restart as Ollama's FAQ describes:
sudo systemctl daemon-reload
sudo systemctl restart ollama
Do not forward a port of your block straight to 11434 either. Forwarded traffic arrives on the server's network interface, so it would need OLLAMA_HOST=0.0.0.0, the setup this guide avoids.
Put Caddy in front with a key
Add the dedicated IPv4 to the interface and to the network configuration, as in the self-host guide, and create a DNS A record for
ollama.example.compointing at it. VeloxaHost has no IPv6 in US East, so add no AAAA record.ip addr add 198.51.100.5/32 dev eth0Install Caddy from its apt repository, as Caddy's install docs give it:
sudo apt install --yes debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddyGenerate a key (ours):
openssl rand -hex 32Replace
/etc/caddy/Caddyfilewith this (ours, built from Caddy's documentedheaderandnotmatchers and itsrespondandreverse_proxydirectives). TheHostline is the one in Ollama's own proxy example:ollama.example.com { @no_key not header Authorization "Bearer <your key>" respond @no_key 401 reverse_proxy localhost:11434 { header_up Host localhost:11434 header_up -Authorization } }Caddy runs
respondbeforereverse_proxyin its fixed directive order, so a request without the exact key gets a401and never reaches Ollama.header_up -Authorizationdrops the key before the request goes upstream, since Ollama does not use it. The key sits in the Caddyfile, so treat that file as a secret.Optionally, accept only known networks as well. Caddy's
client_ipmatcher withabortcloses every other connection (ours, from Caddy's matcher example). Add these two lines at the top of the site block, with your own ranges:@outsiders not client_ip 203.0.113.0/24 abort @outsidersReload Caddy:
sudo systemctl reload caddy
Caddy gets a Let's Encrypt certificate on its own once the A record points at the server and the ports are reachable.
Set the firewall
VeloxaHost firewalls are network-level and stateful. In the console, go to Firewalls, New Firewall, Add Rule. Allow TCP 22 only from YOUR.IP/32, TCP 80 from 0.0.0.0/0, and TCP 443 from 0.0.0.0/0, or only from your clients' addresses if they are fixed. Attach the firewall on the server's page; until one is attached, all traffic is allowed.
Keep port 80 open to everyone (ours, from Caddy's docs): Caddy's HTTP challenge needs port 80 reachable from outside to get and renew the certificate, so it keeps working even with 443 limited to your clients.
Connect your clients
Clients send the key in the Authorization: Bearer header. Ollama's native API and its OpenAI-compatible endpoints under /v1 both work through the proxy. This is Ollama's /v1/chat/completions example, pointed at the proxy with the header added (ours):
curl -X POST https://ollama.example.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your key>" \
-d '{
"model": "llama3.2:3b",
"messages": [{ "role": "user", "content": "Say this is a test" }]
}'
In OpenAI SDKs, set the base URL to https://ollama.example.com/v1/ and the API key to your key. The client sends it as the bearer token. Keep the key on servers and out of browser code, as Ollama's docs advise for its own keys: anything in a web page can be read by its visitors.
Verify it works
Call the list of models without the key, then with it:
curl -i https://ollama.example.com/api/tags
curl -H "Authorization: Bearer <your key>" https://ollama.example.com/api/tags
The first returns 401. The second returns your models as JSON. Then confirm that Ollama itself is not reachable from outside: from your own computer, a request to port 11434 on the server's address should time out (ours):
curl -m 5 http://198.51.100.5:11434/api/tags
Troubleshooting
A 401 with the right key. The header has to be exactly Bearer, one space, then the key, matching the Caddyfile. Check for a stray space or a missing reload.
Errors from Ollama through Caddy, but curl localhost:11434 works. Check the header_up Host localhost:11434 line, which Ollama's proxy example sets.
No certificate. Check that the A record resolves to the dedicated IPv4, the address is on the interface, and the firewall allows port 80 from everyone. Then read journalctl -u caddy.
The API returns 503. That comes from Ollama, not the proxy: its queue (OLLAMA_MAX_QUEUE, 512 by default) is full.
FAQ
Does Ollama have its own API keys?
Only for its cloud. Ollama's docs say direct cloud inference at https://ollama.com/api requires an API key, while the local API needs no authentication. A server you run gets its key check from the proxy in front of it.
Why not open port 11434 and limit it in the firewall?
A firewall rule limits where connections come from, not who makes them, and the traffic to Ollama stays unencrypted. The proxy adds HTTPS and a key, and the firewall can still limit addresses on top.
Can I give each client its own key?
Not with this short Caddyfile, which checks one key. Rotating it means editing the file, reloading Caddy and updating every client. For per-user accounts, put an application with its own users in front, such as Open WebUI.
Is basic_auth or a bearer key better?
Both are checked by Caddy over HTTPS. Use basic_auth from the self-host guide when people call the API with curl or a browser; use a bearer key when the callers are SDKs and tools built for the OpenAI API.
Commands and settings checked on 2026-10-19 against the official documentation: docs.ollama.com, caddyserver.com. Software changes; if a step no longer matches, the vendor's docs win.
Ready to deploy?
Start a free trial, or compare plans to match your team's scale.