Ollama atrás de um proxy reverso com Caddy ou Nginx para streaming HTTPS

HTTPS Ollama sem interromper as respostas em streaming.

Conteúdo da página

Executar o Ollama atrás de um proxy reverso é a maneira mais simples de obter HTTPS, controle de acesso opcional e comportamento de streaming previsível.

Este post foca no ingresso do Caddy e Nginx para a API do Ollama, não no código do cliente.

ollama behind proxy

Se você já tem clientes em Python ou Go comunicando-se com o Ollama, este post é a peça que faltava: ingresso e transporte para a mesma API.

Para saber como o Ollama se encaixa ao lado do vLLM, Docker Model Runner, LocalAI e as compensações de hospedagem em nuvem, veja LLM Hosting in 2026: Local, Self-Hosted & Cloud Infrastructure Compared.

Para exemplos de solicitações e código do cliente, veja Ollama CLI Cheatsheet.

Para a interface de usuário e camadas multi-usuário, veja Open WebUI overview, quickstart and alternatives.

Para o panorama geral sobre auto-hospedagem e controle de dados, veja LLM self-hosting and AI sovereignty.

Para um serviço Ollama de nó único reprodutível no Docker Compose (volumes persistentes, OLLAMA_HOST, GPUs NVIDIA, atualizações), veja Ollama in Docker Compose with GPU and Persistent Model Storage.

Para dispositivos remotos sem portas de entrada públicas (Tailscale, WireGuard, binding, firewall), veja Remote Ollama access via Tailscale or WireGuard, no public ports.

Por que você deve colocar o Ollama atrás de um proxy em vez de expor a porta 11434

O Ollama foi projetado para rodar localmente primeiro. De fábrica, ele se liga ao localhost na porta 11434, o que é ótimo para uma estação de trabalho de desenvolvedor e uma dica nada sutil de que a porta bruta não foi feita para estar voltada para a internet.

Trato a porta 11434 como uma API interna de alto custo. Se estiver acessível a partir da internet pública, qualquer pessoa que a encontre pode queimar o tempo do seu CPU ou GPU, encher seu disco baixando modelos ou simplesmente manter conexões abertas até que algo atinja o tempo limite. Um proxy reverso não torna o Ollama mais seguro por mágica, mas oferece um lugar para colocar os controles que importam na borda: TLS, autenticação, tempos de espera, limites de taxa e logs.

Isso importa porque a API local do Ollama não vem com uma camada de autenticação incorporada. Se você a expuser, tipicamente adiciona autenticação na borda ou mantém-a privada e acessível apenas através de uma rede confiável.

A segunda razão é a experiência do usuário (UX). O Ollama transmite respostas em streaming por padrão. Se o proxy fizer buffer ou compressão no lugar errado, o streaming parecerá quebrado e as interfaces de usuário parecerão estar “pensando” sem saída.

Arquitetura mínima e estratégia de ligação

Uma arquitetura mínima limpa parece com isso:

Client (curl, Python, Go, UI)
        |
        | HTTPS (optional Basic Auth or SSO)
        v
Reverse proxy (Caddy or Nginx)
        |
        | HTTP (private LAN, localhost, or Docker network)
        v
Ollama server (ollama serve on 127.0.0.1:11434)

Duas regras práticas mantêm isso chato da melhor maneira possível.

Primeiro, mantenha o Ollama privado e mova a exposição para o proxy. Se o Caddy ou Nginx rodarem no mesmo host, faça o proxy para 127.0.0.1:11434 e não altere o endereço de ligação do Ollama. Se o proxy rodar em outro lugar (host separado, VM separada ou rede de contêiner), ligue o Ollama a uma interface privada, não a 0.0.0.0 na NIC pública, e confie em um firewall.

Segundo, decida cedo se os navegadores chamarão o Ollama diretamente. Se uma ferramenta baseada em navegador atingir o Ollama de uma origem diferente, você pode precisar lidar com CORS. Se tudo for servido de um único domínio via o proxy (recomendado para sanidade), você pode frequentemente evitar o CORS completamente e manter o Ollama estrito.

Configurações de proxy reverso para streaming e WebSockets

A API do Ollama é HTTP regular, e seu streaming é JSON delimitado por novas linhas (NDJSON). Isso significa que você quer um proxy que possa fazer três coisas bem:

  • Não fazer buffer de respostas em streaming.
  • Não matar solicitações de longa duração apenas porque o modelo demorou para começar a falar.
  • Se uma UI usar WebSockets (algumas usam), encaminhe o Upgrade limpa.

Você pode manter isso simples. Em muitos casos, “manuseio correto de WebSockets” é apenas ter uma configuração que é segura para Upgrade, mesmo que o upstream não use WebSockets hoje.

Exemplo de Caddyfile do Caddy

O Caddy é a opção de “menos configuração, mais padrões”. Se você colocar um nome de domínio público no endereço do site, o Caddy tipicamente obterá e renovará certificados automaticamente.

Configurações mínimas de proxy reverso, HTTPS e amigáveis ao streaming:

# ollama.example.com A/AAAA -> your proxy host
ollama.example.com {

    # Optional Basic Auth at the edge.
    # Generate a password hash with:
    #   caddy hash-password --algorithm bcrypt
    #
    # basic_auth {
    #   alice $2a$12$REDACTED...
    # }

    reverse_proxy 127.0.0.1:11434 {

        # Some setups prefer pinning the upstream Host header.
        # Ollama's own docs show this pattern for Nginx.
        header_up Host localhost:11434

        # For streaming or chat-like workloads, prefer low latency.
        # NDJSON streaming usually flushes immediately anyway, but this makes it explicit.
        flush_interval -1

        transport http {
            # Avoid upstream gzip negotiation if it interferes with streaming.
            compression off

            # Give Ollama time to load a model and produce the first chunk.
            response_header_timeout 10m
            dial_timeout 10s
        }
    }
}

Se você já tem um gateway SSO (oauth2-proxy, Authelia, authentik outpost, etc), o Caddy tem uma diretiva de autenticação direta opinativa. O padrão é “autenticação primeiro, depois proxy”:

ollama.example.com {
    forward_auth 127.0.0.1:4180 {
        uri /oauth2/auth
        # Copy the identity headers your gateway returns, if you need them.
        copy_headers X-Auth-Request-User X-Auth-Request-Email Authorization
    }

    reverse_proxy 127.0.0.1:11434
}

Exemplo de bloco de servidor Nginx

O Nginx lhe dá um pouco mais de corda. O lado positivo é que as opções são explícitas e ele tem primitivas incorporadas para limitação de taxa e limitação de conexão. A armadilha é o buffer: o Nginx faz buffer de respostas proxadas por padrão, que é o oposto do que você quer para streaming NDJSON.

Este exemplo inclui:

  • Redirecionamento de HTTP para HTTPS
  • Caminhos de certificados TLS (estilo Certbot)
  • Encaminhamento de Upgrade seguro para WebSocket
  • proxy_buffering off amigável ao streaming
  • Tempos de espera mais longos do que o padrão de 60s
# /etc/nginx/conf.d/ollama.conf

# WebSocket-safe Connection header handling
map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

# Optional request rate limiting (IP-based)
# 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;

    # Optional Basic Auth at the edge.
    # auth_basic "Ollama";
    # auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        # Optional rate limit
        # limit_req zone=ollama_rate burst=20 nodelay;

        proxy_pass http://127.0.0.1:11434;

        # Match Ollama docs pattern when proxying to localhost.
        proxy_set_header Host localhost:11434;

        # WebSocket Upgrade handling (harmless if unused).
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Critical for NDJSON streaming.
        proxy_buffering off;

        # Prevent 60s idle timeouts while waiting for tokens.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Se você quiser um portão estilo SSO no Nginx, o padrão equivalente é auth_request. O Nginx envia uma sub-solicitação para seu serviço de autenticação e só faz proxy para o Ollama quando a autenticação retorna 2xx.

Armadilhas de automação e renovação de TLS

Para TLS, a divisão operacional é simples.

Com o Caddy, o TLS geralmente é “parte do proxy reverso”. HTTPS automático é um de seus recursos principais, então a emissão e renovação de certificados estão acopladas a manter o Caddy rodando, ter DNS funcionando e expor as portas 80 e 443.

Com o Nginx, o TLS geralmente é “um cliente ACME separado mais Nginx”. O modo de falha comum não é criptografia, é encanamento:

  • Porta 80 não acessível para desafios HTTP-01.
  • Certificados armazenados em um contêiner mas não persistidos.
  • Limites de taxa ao fazer instalações novas repetidas ou implantações de teste.

Um ponto sutil que importa para serviços de longa duração é que a vida útil dos certificados é curta por design. Trate as renovações como um requisito de automação de fundo, não como um evento anual no calendário.

Autenticação, controle de abuso e verificação

Esta é a parte que faz um endpoint de LLM voltado para a internet parecer profissional.

Opções de autenticação, da bruta à elegante

Autenticação Básica no proxy é bruta, mas surpreendentemente eficaz para um endpoint privado. Também é fácil de aplicar tanto a solicitações HTTP quanto a upgrades de WebSocket.

Se você quer fluxos de login amigáveis ao navegador, forward auth e auth_request são o padrão comum. Seu proxy permanece sem estado e um gateway de autenticação gerencia sessões e MFA. A compensação é mais partes móveis.

Se você já está rodando Open WebUI, também pode confiar na sua autenticação em nível de aplicativo e manter o próprio Ollama privado. O proxy então protege o Open WebUI, não o Ollama diretamente.

Se você não precisa de acesso público de forma alguma, uma abordagem apenas de rede pode ser mais limpa. Por exemplo, o Tailscale Serve pode expor um serviço local dentro do seu tailnet sem abrir portas de entrada no seu roteador. Para padrões completos (WireGuard, OLLAMA_HOST em interfaces VPN, pinagem de firewall, lista de verificação de segurança), veja Remote Ollama access via Tailscale or WireGuard, no public ports.

Básicos de abuso para uma API cara

O Ollama é uma poderosa API local, e sua superfície vai além da geração. Ele tem endpoints para chat, embeddings, listagem de modelos e verificações de versão. Trate toda a API como sensível.

Referência oficial da API (endpoints e streaming): https://docs.ollama.com/api

Na camada de proxy, há três controles de baixo esforço que reduzem a dor do primeiro dia:

  • Limitação de taxa por IP em endpoints de geração.
  • Limites de conexão para impedir que um pequeno número de clientes mantenha tudo aberto.
  • Tempos de espera conservadores que correspondam à realidade do seu modelo e hardware, não padrões web genéricos.

Na camada do Ollama, também pode rejeitar sobrecarga com 503 e tem opções de servidor para enfileiramento. A limitação de taxa do proxy impede que você chegue lá com tanta frequência.

Lista de verificação de verificação

Use os mesmos checks que você usaria para qualquer API de streaming.

  1. Conectividade básica e TLS

    • curl -sS https://ollama.example.com/api/version
    • curl -sS https://ollama.example.com/api/tags | head
  2. Streaming funciona de ponta a ponta (sem buffer)

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

    Se você estiver atrás de Autenticação Básica:

    • 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. Sanidade da UI do navegador

    • Carregue sua UI de chat e acione uma resposta.
    • Se a UI usar WebSockets, confirme que você não vê erros 400 ou 426 e a conexão permanece aberta durante a geração.

Se a saída do curl aparecer apenas no final, quase sempre é buffer no proxy. Reverifique proxy_buffering off no Nginx e considere forçar o flush de baixa latência no Caddy para o bloco de site do Ollama.