Ollama dietro un reverse proxy con Caddy o Nginx per lo streaming HTTPS
HTTPS per Ollama senza interrompere le risposte in streaming.
Eseguire Ollama dietro un proxy inverso è il modo più semplice per ottenere HTTPS, un controllo degli accessi opzionale e un comportamento di streaming prevedibile.
Questo post si concentra sull’ingresso Caddy e Nginx per l’API Ollama, non sul codice client.

Se hai già client Python o Go che comunicano con Ollama, questo post è il pezzo mancante: ingress e trasporto per la stessa API.
Per capire come Ollama si inserisce accanto a vLLM, Docker Model Runner, LocalAI e i compromessi dell’hosting cloud, vedi Hosting LLM nel 2026: Locale, Self-Hosted e Cloud Infrastructure Confrontati.
Per esempi di richieste e codice client, vedi Ollama CLI Cheatsheet.
Per livelli UI e multi-utente, vedi Panoramica, quickstart e alternative di Open WebUI.
Per una visione d’insieme su self-hosting e controllo dei dati, vedi Self-hosting LLM e sovranità AI.
Per un servizio Ollama a singolo nodo riproducibile in Docker Compose (volumi persistenti, OLLAMA_HOST, GPU NVIDIA, aggiornamenti), vedi
Ollama in Docker Compose con GPU e Archiviazione Persistente dei Modelli.
Per dispositivi remoti senza porte in ingresso pubbliche (Tailscale, WireGuard, binding, firewall), vedi Accesso remoto a Ollama tramite Tailscale o WireGuard, senza porte pubbliche.
Perché dovresti usare un proxy per Ollama invece di esporre la porta 11434
Ollama è progettato per funzionare prima di tutto in locale. Di default si lega a localhost sulla porta 11434, il che è ottimo per una workstation di sviluppo e un indizio non così sottile che la porta grezza non è destinata ad essere esposta a Internet.
Considero la porta 11434 come un’API interna ad alto costo. Se è raggiungibile dal pubblico Internet, chiunque la trovi può consumare il tempo della tua CPU o GPU, riempire il disco scaricando modelli o semplicemente mantenere le connessioni aperte fino a un timeout. Un proxy inverso non rende Ollama più sicuro per magia, ma ti dà un punto dove posizionare i controlli che contano al livello del confine: TLS, autenticazione, timeout, limiti di velocità e log.
Questo è importante perché l’API locale di Ollama non viene fornita con un layer di autenticazione integrato. Se la esponi, tipicamente aggiungi l’autenticazione al livello del confine o la tieni privata e raggiungibile solo tramite una rete fidata.
Il secondo motivo è l’esperienza utente (UX). Ollama trasmette le risposte in streaming di default. Se il proxy bufferizza o comprime nel punto sbagliato, lo streaming sembra rotto e le UI sembrano “pensare” senza output.
Architettura minima e strategia di binding
Un minimo pulito appare così:
Client (curl, Python, Go, UI)
|
| HTTPS (opzionale Basic Auth o SSO)
v
Proxy inverso (Caddy o Nginx)
|
| HTTP (LAN privata, localhost o rete Docker)
v
Server Ollama (ollama serve su 127.0.0.1:11434)
Due regole pratiche mantengono tutto noioso nel miglior modo possibile.
Prima, mantieni Ollama privato e sposta l’esposizione sul proxy. Se Caddy o Nginx girano sullo stesso host, fai il proxy verso 127.0.0.1:11434 e non cambiare l’indirizzo di binding di Ollama. Se il proxy gira altrove (host separato, VM separata o rete container), lega Ollama a un’interfaccia privata, non a 0.0.0.0 sulla NIC pubblica, e affidati a un firewall.
Secondo, decidi presto se i browser chiameranno Ollama direttamente. Se uno strumento basato su browser colpisce Ollama da un’origine diversa, potresti dover gestire CORS. Se tutto viene servito da un unico dominio tramite il proxy (consigliato per sanità mentale), puoi spesso evitare completamente CORS e mantenere Ollama rigoroso.
Configurazioni proxy inverso per streaming e WebSockets
L’API di Ollama è HTTP regolare e il suo streaming è JSON delimitato da newline (NDJSON). Questo significa che vuoi un proxy che sappia fare bene tre cose:
- Non bufferizzare le risposte in streaming.
- Non uccidere richieste di lunga durata solo perché il modello ha impiegato un po’ a rispondere.
- Se una UI usa WebSockets (alcune lo fanno), inoltra l’Upgrade in modo pulito.
Puoi mantenere questo semplice. In molti casi, “gestione corretta dei WebSockets” significa avere una configurazione sicura per l’Upgrade anche se l’upstream non usa WebSockets oggi.
Esempio Caddyfile per Caddy
Caddy è l’opzione “meno configurazione, più default”. Se inserisci un nome di dominio pubblico nell’indirizzo del sito, Caddy otterrà e rinnoverà automaticamente i certificati.
Impostazioni minime per proxy inverso, HTTPS e amichevoli per lo streaming:
# ollama.example.com A/AAAA -> il tuo host proxy
ollama.example.com {
# Opzionale Basic Auth al confine.
# Genera un hash della password con:
# caddy hash-password --algorithm bcrypt
#
# basic_auth {
# alice $2a$12$REDACTED...
# }
reverse_proxy 127.0.0.1:11434 {
# Alcuni setup preferiscono fissare l'header Host upstream.
# I documenti di Ollama mostrano questo pattern per Nginx.
header_up Host localhost:11434
# Per carichi di lavoro di streaming o chat, preferisci bassa latenza.
# Lo streaming NDJSON solitamente scarica immediatamente comunque, ma questo lo rende esplicito.
flush_interval -1
transport http {
# Evita la negoziazione gzip upstream se interferisce con lo streaming.
compression off
# Dai a Ollama il tempo di caricare un modello e produrre il primo chunk.
response_header_timeout 10m
dial_timeout 10s
}
}
}
Se hai già un gateway SSO (oauth2-proxy, Authelia, authentik outpost, ecc.), Caddy ha una direttiva di autenticazione in avanti opinata. Il pattern è “prima auth, poi proxy”:
ollama.example.com {
forward_auth 127.0.0.1:4180 {
uri /oauth2/auth
# Copia gli header di identità che il tuo gateway restituisce, se li hai bisogno.
copy_headers X-Auth-Request-User X-Auth-Request-Email Authorization
}
reverse_proxy 127.0.0.1:11434
}
Esempio blocco server Nginx
Nginx ti dà un po’ più di spazio. Il lato positivo è che le manopole sono esplicite e ha primitive integrate per il rate limiting e il limite di connessioni. Il “piede nella mina” è il buffering: Nginx bufferizza le risposte proxy di default, che è l’opposto di quello che vuoi per lo streaming NDJSON.
Questo esempio include:
- Reindirizzamento da HTTP a HTTPS
- Percorsi certificati TLS (stile Certbot)
- Inoltro Upgrade sicuro per WebSocket
- proxy_buffering off amichevole per lo streaming
- Timeout più lunghi del default di 60s
# /etc/nginx/conf.d/ollama.conf
# Gestione header Connection sicura per WebSocket
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
# Opzionale limitazione velocità richieste (basata su IP)
# limit_req_zone $binary_remote_addr zone=ollama_rate:10m rate=10r/s;
server {
listen 80;
server_name ollama.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name ollama.example.com;
ssl_certificate /etc/letsencrypt/live/ollama.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ollama.example.com/privkey.pem;
# Opzionale Basic Auth al confine.
# auth_basic "Ollama";
# auth_basic_user_file /etc/nginx/.htpasswd;
location / {
# Opzionale limitazione velocità
# limit_req zone=ollama_rate burst=20 nodelay;
proxy_pass http://127.0.0.1:11434;
# Corrispondenza pattern documenti Ollama quando si fa proxy verso localhost.
proxy_set_header Host localhost:11434;
# Gestione Upgrade WebSocket (innocua se non usata).
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Critico per lo streaming NDJSON.
proxy_buffering off;
# Previene timeout idle di 60s mentre si aspettano i token.
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Se vuoi una porta stile SSO in Nginx, il pattern equivalente è auth_request. Nginx invia una sotto-richiesta al tuo servizio di autenticazione e fa proxy verso Ollama solo quando l’autenticazione restituisce 2xx.
Automazione TLS e insidie del rinnovo
Per il TLS, la divisione operativa è semplice.
Con Caddy, il TLS è solitamente “parte del proxy inverso”. HTTPS automatico è una delle sue caratteristiche principali, quindi l’emissione e il rinnovo dei certificati sono accoppiati al mantenere Caddy in esecuzione, avere DNS funzionante ed esporre le porte 80 e 443.
Con Nginx, il TLS è solitamente “un client ACME separato più Nginx”. Il modo di fallimento comune non è la crittografia, è l’impiantistica:
- Porta 80 non raggiungibile per le sfide HTTP-01.
- Certificati memorizzati in un container ma non persistiti.
- Limiti di velocità quando si fanno installazioni fresche ripetute o deploy di test.
Un punto sottile che conta per servizi a lunga durata è che la durata dei certificati è breve per design. Tratta i rinnovi come un requisito di automazione di background, non come un evento annuale del calendario.
Autenticazione, controllo abuso e verifica
Questa è la parte che fa sembrare professionale un endpoint LLM esposto a Internet.
Opzioni di autenticazione, dal diretto all’elegante
Basic Auth sul proxy è diretto, ma sorprendentemente efficace per un endpoint privato. È anche facile da applicare sia alle richieste HTTP che agli upgrade WebSocket.
Se vuoi flussi di login amichevoli per il browser, forward auth e auth_request sono il pattern comune. Il tuo proxy rimane stateless e un gateway di autenticazione gestisce sessioni e MFA. Il compromesso è più parti mobili.
Se stai già utilizzando Open WebUI, puoi anche affidarti alla sua autenticazione a livello di applicazione e mantenere Ollama stesso privato. Il proxy protegge quindi Open WebUI, non Ollama direttamente.
Se non hai bisogno di accesso pubblico affatto, un approccio solo di rete può essere più pulito. Ad esempio, Tailscale Serve può esporre un servizio locale all’interno della tua tailnet senza aprire porte in ingresso sul tuo router. Per pattern completi (WireGuard, OLLAMA_HOST su interfacce VPN, fissaggio firewall, checklist di sicurezza), vedi Accesso remoto a Ollama tramite Tailscale o WireGuard, senza porte pubbliche.
Basi di abuso per un’API costosa
Ollama è una potente API locale e la sua superficie va oltre la generazione. Ha endpoint per chat, embeddings, elenco modelli e controlli versione. Tratta tutta l’API come sensibile.
Riferimento API ufficiale (endpoint e streaming): https://docs.ollama.com/api
Al livello del proxy, ci sono tre controlli a basso sforzo che riducono il dolore del primo giorno:
- Rate limiting per IP sugli endpoint di generazione.
- Limiti di connessione per fermare un piccolo numero di client che tengono tutto aperto.
- Timeout conservativi che corrispondono alla realtà del tuo modello e hardware, non default web generici.
Al livello di Ollama, può anche rifiutare il sovraccarico con 503 e ha manopole lato server per la coda. Il rate limiting del proxy ti tiene lontano da lì più spesso.
Checklist di verifica
Usa gli stessi controlli che useresti per qualsiasi API streaming.
-
Connettività di base e TLS
curl -sS https://ollama.example.com/api/versioncurl -sS https://ollama.example.com/api/tags | head
-
Streaming funziona da capo a fondo (niente buffering)
curl -N https://ollama.example.com/api/generate -H "Content-Type: application/json" -d '{"model":"mistral","prompt":"Write 10 words only.","stream":true}'
Se sei dietro Basic Auth:
curl -N -u alice:REDACTED https://ollama.example.com/api/generate -H "Content-Type: application/json" -d '{"model":"mistral","prompt":"Write 10 words only.","stream":true}'
-
Sanità UI del browser
- Carica la tua UI chat e innesca una risposta.
- Se la UI usa WebSockets, conferma di non vedere errori 400 o 426 e che la connessione rimane aperta durante la generazione.
Se l’output di curl appare solo alla fine, è quasi sempre buffering al proxy. Ricontrolla proxy_buffering off in Nginx e considera di forzare lo scaricamento a bassa latenza in Caddy per il blocco del sito Ollama.