Ollama derrière un proxy inversé avec Caddy ou Nginx pour le streaming HTTPS

HTTPS Ollama sans interrompre les réponses en flux.

Sommaire

Exécuter Ollama derrière un proxy inversé est le moyen le plus simple d’obtenir HTTPS, un contrôle d’accès facultatif et un comportement de streaming prévisible.

Cet article se concentre sur l’ingress Caddy et Nginx pour l’API Ollama, et non sur le code client.

ollama behind proxy

Si vous avez déjà des clients Python ou Go communiquant avec Ollama, cet article est la pièce manquante : l’ingress et le transport pour la même API.

Pour savoir comment Ollama s’intègre aux côtés de vLLM, Docker Model Runner, LocalAI et des compromis d’hébergement cloud, consultez LLM Hosting in 2026: Local, Self-Hosted & Cloud Infrastructure Compared.

Pour des exemples de requêtes et du code client, consultez Ollama CLI Cheatsheet.

Pour les couches d’interface utilisateur et multi-utilisateurs, consultez Open WebUI overview, quickstart and alternatives.

Pour une vue d’ensemble sur l’auto-hébergement et le contrôle des données, consultez LLM self-hosting and AI sovereignty.

Pour un service Ollama sur un nœud unique reproductible dans Docker Compose (volumes persistants, OLLAMA_HOST, GPU NVIDIA, mises à jour), consultez Ollama in Docker Compose with GPU and Persistent Model Storage.

Pour les appareils distants sans ports entrants publics (Tailscale, WireGuard, liaison, pare-feu), consultez Remote Ollama access via Tailscale or WireGuard, no public ports.

Pourquoi vous devriez proxifier Ollama plutôt que d’exposer le port 11434

Ollama est conçu pour s’exécuter localement en premier lieu. Par défaut, il se lie à localhost sur le port 11434, ce qui est idéal pour une station de travail de développeur et constitue un indice non subtile indiquant que le port brut n’est pas destiné à être exposé sur Internet.

Je considère le port 11434 comme une API interne à coût élevé. S’il est accessible depuis Internet public, quiconque le trouve peut consumer votre temps CPU ou GPU, remplir votre disque en téléchargeant des modèles, ou simplement maintenir des connexions ouvertes jusqu’à ce qu’une expiration se produise. Un proxy inversé ne rend pas Ollama plus sûr par magie, mais il vous offre un endroit pour placer les contrôles essentiels en bordure : TLS, authentification, temporisations, limites de débit et journaux.

Cela est important car l’API locale d’Ollama n’est pas livrée avec une couche d’authentification intégrée. Si vous l’exposez, vous devez généralement ajouter l’authentification en bordure ou la maintenir privée et accessible uniquement via un réseau de confiance.

La deuxième raison est l’expérience utilisateur (UX). Ollama diffuse les réponses par défaut. Si le proxy tamponne ou compresse à mauvais endroit, le streaming semble cassé et les interfaces utilisateur semblent « réfléchir » sans produire de sortie.

Architecture minimale et stratégie de liaison

Une architecture minimale propre ressemble à ceci :

Client (curl, Python, Go, UI)
        |
        | HTTPS (authentification Basic ou SSO facultative)
        v
Proxy inversé (Caddy ou Nginx)
        |
        | HTTP (LAN privé, localhost ou réseau Docker)
        v
Serveur Ollama (ollama serve sur 127.0.0.1:11434)

Deux règles pratiques maintiennent cela ennuyeux, de la meilleure des façons.

Premièrement, gardez Ollama privé et déplacez l’exposition vers le proxy. Si Caddy ou Nginx s’exécutent sur le même hôte, proxifiez vers 127.0.0.1:11434 et ne changez pas l’adresse de liaison d’Ollama. Si le proxy s’exécute ailleurs (hôte séparé, VM séparée ou réseau de conteneur), liez Ollama à une interface privée, pas à 0.0.0.0 sur l’interface réseau publique, et comptez sur un pare-feu.

Deuxièmement, décidez tôt si les navigateurs appelleront Ollama directement. Si un outil basé sur le navigateur touche Ollama depuis une origine différente, vous devrez peut-être gérer les CORS. Si tout est servi depuis un domaine unique via le proxy (recommandé pour la santé mentale), vous pouvez souvent éviter complètement les CORS et garder Ollama strict.

Configurations de proxy inversé pour le streaming et WebSockets

L’API d’Ollama est du HTTP standard, et son streaming est du JSON délimité par des nouvelles lignes (NDJSON). Cela signifie que vous voulez un proxy capable de faire trois choses bien :

  • Ne pas tamponner les réponses en streaming.
  • Ne pas tuer les requêtes de longue durée simplement parce que le modèle a mis du temps à répondre.
  • Si une interface utilisateur utilise WebSockets (certains le font), transmettre Upgrade proprement.

Vous pouvez garder cela simple. Dans de nombreux cas, « une gestion correcte des WebSockets » consiste simplement à avoir une configuration sûre pour Upgrade, même si l’amont n’utilise pas WebSockets aujourd’hui.

Exemple de fichier Caddyfile

Caddy est l’option « moins de configuration, plus de paramètres par défaut ». Si vous placez un nom de domaine public dans l’adresse du site, Caddy obtiendra et renouvellera généralement les certificats automatiquement.

Paramètres de proxy inversé minimal, HTTPS et compatibles streaming :

# ollama.example.com A/AAAA -> votre hôte proxy
ollama.example.com {

    # Authentification Basic facultative en bordure.
    # Générez un hash de mot de passe avec :
    #   caddy hash-password --algorithm bcrypt
    #
    # basic_auth {
    #   alice $2a$12$REDACTED...
    # }

    reverse_proxy 127.0.0.1:11434 {

        # Certains configurations préfèrent fixer l'en-tête Host amont.
        # La propre documentation d'Ollama montre ce motif pour Nginx.
        header_up Host localhost:11434

        # Pour les charges de travail de streaming ou de type chat, préférez une faible latence.
        # Le streaming NDJSON flush généralement immédiatement de toute façon, mais cela le rend explicite.
        flush_interval -1

        transport http {
            # Évitez la négociation gzip amont si elle interfère avec le streaming.
            compression off

            # Donnez à Ollama le temps de charger un modèle et de produire le premier chunk.
            response_header_timeout 10m
            dial_timeout 10s
        }
    }
}

Si vous avez déjà une passerelle SSO (oauth2-proxy, Authelia, outpost authentik, etc.), Caddy dispose d’une directive d’authentification forward d’opinion. Le motif est « authentification d’abord, puis proxy » :

ollama.example.com {
    forward_auth 127.0.0.1:4180 {
        uri /oauth2/auth
        # Copiez les en-têtes d'identité que votre passerelle retourne, si vous en avez besoin.
        copy_headers X-Auth-Request-User X-Auth-Request-Email Authorization
    }

    reverse_proxy 127.0.0.1:11434
}

Exemple de bloc serveur Nginx

Nginx vous donne un peu plus de cordes. L’avantage est que les commandes sont explicites, et il possède des primitives intégrées pour la limitation de débit et la limitation de connexion. Le piège est le tamponnage : Nginx tamponne les réponses proxifiées par défaut, ce qui est le contraire de ce que vous voulez pour le streaming NDJSON.

Cet exemple inclut :

  • Redirection HTTP vers HTTPS
  • Chemins de certificats TLS (style Certbot)
  • Transfert Upgrade compatible WebSocket
  • proxy_buffering off pour un streaming compatible
  • Des temporisations plus longues que les 60s par défaut
# /etc/nginx/conf.d/ollama.conf

# Gestion de l'en-tête Connection compatible WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

# Limitation de débit des requêtes facultative (basée sur 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;

    # Authentification Basic facultative en bordure.
    # auth_basic "Ollama";
    # auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        # Limitation de débit facultative
        # limit_req zone=ollama_rate burst=20 nodelay;

        proxy_pass http://127.0.0.1:11434;

        # Correspondre au motif des docs Ollama lors de la proxification vers localhost.
        proxy_set_header Host localhost:11434;

        # Gestion de l'Upgrade WebSocket (inoffensif si non utilisé).
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Critique pour le streaming NDJSON.
        proxy_buffering off;

        # Empêcher les temporisations d'inactivité de 60s en attendant les tokens.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Si vous voulez une porte de style SSO dans Nginx, le motif équivalent est auth_request. Nginx envoie une sous-requête à votre service d’authentification et ne proxifie vers Ollama que lorsque l’authentification retourne 2xx.

Pièges de l’automatisation et du renouvellement TLS

Pour le TLS, la division opérationnelle est simple.

Avec Caddy, le TLS fait généralement « partie du proxy inversé ». Le HTTPS automatique est l’une de ses fonctionnalités phares, donc l’émission et le renouvellement des certificats sont couplés au fait de maintenir Caddy en exécution, d’avoir un DNS fonctionnel et d’exposer les ports 80 et 443.

Avec Nginx, le TLS est généralement « un client ACME séparé plus Nginx ». Le mode de défaillance commun n’est pas la cryptographie, c’est la plomberie :

  • Le port 80 n’est pas accessible pour les défis HTTP-01.
  • Les certificats stockés dans un conteneur mais non persistés.
  • Limites de débit lors de répétitions d’installations fraîches ou de déploiements de test.

Un point subtil qui compte pour les services à longue durée de vie est que les durées de vie des certificats sont courtes par conception. Traitez les renouvellements comme une exigence d’automatisation en arrière-plan, et non comme un événement annuel sur un calendrier.

Authentification, contrôle des abus et vérification

C’est la partie qui donne à un point de terminaison LLM exposé sur Internet un aspect professionnel.

Options d’authentification, du brutal à l’élégant

L’authentification Basic au niveau du proxy est brutale, mais surprenamment efficace pour un point de terminaison privé. Elle est également facile à appliquer à la fois aux requêtes HTTP et aux mises à niveau WebSocket.

Si vous voulez des flux de connexion amicaux pour les navigateurs, forward auth et auth_request sont le motif commun. Votre proxy reste sans état, et une passerelle d’authentification gère les sessions et la MFA. Le compromis est plus de pièces mobiles.

Si vous exécutez déjà Open WebUI, vous pouvez également vous fier à son authentification au niveau de l’application et garder Ollama lui-même privé. Le proxy protège alors Open WebUI, et non Ollama directement.

Si vous n’avez pas besoin d’accès public du tout, une approche réseau uniquement peut être plus propre. Par exemple, Tailscale Serve peut exposer un service local à l’intérieur de votre tailnet sans ouvrir de ports entrants sur votre routeur. Pour les motifs complets (WireGuard, OLLAMA_HOST sur les interfaces VPN, fixation de pare-feu, liste de contrôle de sécurité), consultez Remote Ollama access via Tailscale or WireGuard, no public ports.

Bases de l’abus pour une API coûteuse

Ollama est une API locale puissante, et sa surface dépasse la génération. Il possède des points de terminaison pour le chat, les embeddings, la liste des modèles et les vérifications de version. Traitez toute l’API comme sensible.

Référence officielle de l’API (points de terminaison et streaming) : https://docs.ollama.com/api

Au niveau du proxy, il existe trois contrôles à faible effort qui réduisent la douleur du premier jour :

  • Limitation de débit par IP sur les points de terminaison de génération.
  • Limites de connexion pour empêcher un petit nombre de clients de tout maintenir ouvert.
  • Des temporisations conservatrices qui correspondent à votre réalité de modèle et de matériel, et non aux paramètres web génériques.

Au niveau d’Ollama, il peut également rejeter la surcharge avec 503 et possède des commandes côté serveur pour la mise en file d’attente. La limitation de débit du proxy vous empêche d’arriver aussi souvent.

Liste de contrôle de vérification

Utilisez les mêmes vérifications que vous utiliseriez pour n’importe quelle API de streaming.

  1. Connectivité de base et TLS

    • curl -sS https://ollama.example.com/api/version
    • curl -sS https://ollama.example.com/api/tags | head
  2. Le streaming fonctionne de bout en bout (pas de tamponnage)

    • curl -N https://ollama.example.com/api/generate -H "Content-Type: application/json" -d '{"model":"mistral","prompt":"Write 10 words only.","stream":true}'

    Si vous êtes derrière une authentification Basic :

    • 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}'
  3. Vérification de l’interface utilisateur du navigateur

    • Chargez votre interface de chat et déclenchez une réponse.
    • Si l’interface utilise WebSockets, confirmez que vous ne voyez pas d’erreurs 400 ou 426 et que la connexion reste ouverte pendant la génération.

Si la sortie curl n’apparaît qu’à la fin, c’est presque toujours un tamponnage au niveau du proxy. Vérifiez à nouveau proxy_buffering off dans Nginx, et envisagez de forcer un flush à faible latence dans Caddy pour le bloc de site Ollama.