Fixing Local LLM HTTP 403 CORS, Reverse Proxy Host Headers & LAN Bearer Authentication
Why browser extensions and LAN web clients get HTTP 403 Forbidden from Ollama—and how to configure OLLAMA_ORIGINS, Host header rewrites, and Bearer token protection safely.
Nginx 1.26 / Caddy 2.8 · Ollama 0.5.7 · Chrome / Firefox Extensions & LAN Web Clients
Keeps raw Ollama bound strictly to 127.0.0.1 while exposing a Bearer-authenticated port to trusted LAN devices.
- Security Hazard: Setting `OLLAMA_HOST=0.0.0.0` AND `OLLAMA_ORIGINS=*` simultaneously without a reverse proxy allows ANY public website you visit in your browser to silently query or delete your local models.
- Modern browsers block HTTPS web apps (e.g. `https://app.example.com`) from calling plain HTTP LAN IPs (`http://192.168.1.50:11434`) due to Mixed Content / Private Network Access policy.
1. Why Ollama Returns HTTP 403 Forbidden (Two Different Root Causes)
| Caller Scenario | Observed Error | Root Cause | Safe Production Fix |
|---|---|---|---|
| Chrome / Firefox AI Extension on Host | HTTP 403 on OPTIONS /api/tags | Origin: chrome-extension://* not whitelisted | Set OLLAMA_ORIGINS="chrome-extension://*,moz-extension://*" |
| Nginx / Caddy Custom Domain Proxy | HTTP 403 even from curl without Origin | Ollama rejects non-localhost Host header | Set proxy_set_header Host localhost:11434; |
| HTTPS Web App -> HTTP LAN IP | Blocked by Mixed Content / PNA in DevTools | Browser blocks HTTPS->HTTP private IP calls | Route via backend server or enable Caddy local TLS |
# 1. Test browser extension Origin check (Expect 204/200 after setting OLLAMA_ORIGINS) curl -i -X OPTIONS http://127.0.0.1:11434/api/tags \ -H "Origin: chrome-extension://abcdefghijklmnop" \ -H "Access-Control-Request-Method: POST" # 2. Test reverse-proxy Host header check curl -i http://127.0.0.1:11434/api/version -H "Host: localhost:11434"
2. Zero-Trust LAN Sharing: Keep Ollama on 127.0.0.1 Behind an Auth Proxy
3. Blocking Destructive Endpoints (`/api/delete`, `/api/pull`) at the Proxy Layer
Frequently Asked Questions & Edge Cases
Q: Why can an HTTPS website call `http://127.0.0.1:11434` in Chrome, but fails when calling `http://192.168.1.50:11434`?
The W3C Secure Contexts specification explicitly exempts loopback addresses (`127.0.0.1` and `localhost`) as potentially trustworthy, allowing HTTPS pages to fetch from local loopback if CORS allows it. RFC1918 LAN IPs (`192.168.x.x`) are NOT exempt and are blocked as Mixed Content unless served over HTTPS.
Q: Why did `export OLLAMA_ORIGINS=...` in my macOS terminal not affect the Ollama menu bar app?
GUI apps launched by macOS Finder/Launchpad do not read `~/.zshrc`. Run `launchctl setenv OLLAMA_ORIGINS "chrome-extension://*,moz-extension://*"` and then quit and reopen the Ollama app from the menu bar.
Secure LAN Reverse Proxy for Ollama / vLLM (Bearer Auth + CORS + Unbuffered SSE)
Protects local port 11434 behind an Nginx reverse proxy with API key authentication, clean CORS headers for browser extensions, and 300s SSE streaming.
# Tinker AI — Secure Nginx Reverse Proxy for Local Ollama (Keep Ollama on 127.0.0.1:11434)
map $http_authorization $auth_ok {
default 0;
"Bearer sk-tinker-homelab-secret-2026" 1;
}
server {
listen 0.0.0.0:11435;
server_name _;
location / {
# Handle browser / Chrome extension CORS preflight
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
return 204;
}
if ($auth_ok = 0) {
return 401 '{"error":"Unauthorized: valid Bearer token required"}';
}
proxy_pass http://127.0.0.1:11434;
proxy_set_header Host localhost:11434;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
chunked_transfer_encoding on;
}
}