TinkerLab
04 // DIAGNOSISStreaming / APIDocker / ComposeApple Silicon MacNVIDIA GPU~8 min read & lab

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.

[KEY VERIFIED TAKEAWAY / TL;DR]In Tinker AI Lab's reproducible verification of "Fixing Local LLM HTTP 403 CORS, Reverse Proxy Host Headers & LAN Bearer Authentication" (tested on Nginx 1.26 / Caddy 2.8 · Ollama 0.5.7 · Chrome / Firefox Extensions & LAN Web Clients with Ollama 0.5.7, Nginx 1.26+, Caddy 2.8+, verified 2026-10-23): 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. Offline capability is classified as "100% Intranet / LAN Authenticated Gateway" (Keeps raw Ollama bound strictly to 127.0.0.1 while exposing a Bearer-authenticated port to trusted LAN devices.), with key operational boundary: 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.
REPRODUCIBILITY MANIFEST // TESTED SPECS & LIMITS
Last Verified: 2026-10-23
Tested Hardware & OS

Nginx 1.26 / Caddy 2.8 · Ollama 0.5.7 · Chrome / Firefox Extensions & LAN Web Clients

Pinned Stack & Model Versions
Ollama 0.5.7Nginx 1.26+Caddy 2.8+
Offline & Network Boundary
100% Intranet / LAN Authenticated Gateway

Keeps raw Ollama bound strictly to 127.0.0.1 while exposing a Bearer-authenticated port to trusted LAN devices.

Known Limits & Failure Modes
  • 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.
Hit an edge-case failure?Open Troubleshooter →

1. Why Ollama Returns HTTP 403 Forbidden (Two Different Root Causes)

When calling Ollama from a browser extension or through a reverse proxy, `HTTP 403 Forbidden` is triggered by one of two built-in security guards: 1. **Origin Header Check (`OLLAMA_ORIGINS`)**: Browser extensions send `Origin: chrome-extension://<id>` or `moz-extension://<id>`. Ollama rejects non-localhost Origins unless whitelisted in `OLLAMA_ORIGINS`. 2. **Host Header Check (DNS Rebinding Protection)**: When Nginx or Caddy forwards `Host: llm.homelab.lan` to `127.0.0.1:11434`, Ollama rejects the unfamiliar Host header with `403`. Rewriting `proxy_set_header Host localhost:11434;` fixes it immediately.
Table 1: Local LLM Browser & Reverse Proxy Error Diagnostic Matrix
Caller ScenarioObserved ErrorRoot CauseSafe Production Fix
Chrome / Firefox AI Extension on HostHTTP 403 on OPTIONS /api/tagsOrigin: chrome-extension://* not whitelistedSet OLLAMA_ORIGINS="chrome-extension://*,moz-extension://*"
Nginx / Caddy Custom Domain ProxyHTTP 403 even from curl without OriginOllama rejects non-localhost Host headerSet proxy_set_header Host localhost:11434;
HTTPS Web App -> HTTP LAN IPBlocked by Mixed Content / PNA in DevToolsBrowser blocks HTTPS->HTTP private IP callsRoute via backend server or enable Caddy local TLS
Reproduce & Verify CORS + Host Header Behavior via cURL
# 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

Because Ollama has no native API key authentication, exposing it directly to your LAN (`OLLAMA_HOST=0.0.0.0`) lets any device on Wi-Fi call `/api/delete` or saturate your GPU. Instead, keep Ollama bound to `127.0.0.1:11434` and run our parameterized Nginx configuration (`nginx-ollama-lan-auth.conf`) on port `11435`—enforcing a secret `Authorization: Bearer <token>`, rewriting `Host: localhost:11434`, and disabling proxy buffering for SSE streams.

3. Blocking Destructive Endpoints (`/api/delete`, `/api/pull`) at the Proxy Layer

Even for authenticated LAN users or shared team tokens, external clients only need `/api/chat`, `/api/generate`, `/api/embeddings`, and `/v1/*`. Add a location rule in Nginx (`location ~ ^/api/(delete|pull|push|create) { return 403; }`) so model management can only be performed via local SSH on the host.
FIELD Q&A // 常见实测疑问与边界解答

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.

REPRODUCIBLE ARTIFACT // 配套可参数化附件

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.

nginx-ollama-lan-auth.conf ↗
Edit variables above to update the configuration live
# 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;
    }
}

More Reproducible Engineering Guides

View All 42 Guides →