Ollama detrás de un proxy inverso con Caddy o Nginx para streaming HTTPS

HTTPS con Ollama sin interrumpir las respuestas en streaming.

Índice

Ejecutar Ollama detrás de un proxy inverso es la forma más sencilla de obtener HTTPS, control de acceso opcional y un comportamiento de transmisión predecible.

Este artículo se centra en la entrada de Caddy y Nginx para la API de Ollama, no en el código del cliente.

ollama detrás de proxy

Si ya tienes clientes en Python o Go comunicándose con Ollama, este artículo es la pieza que faltaba: la entrada y el transporte para la misma API.

Para saber cómo encaja Ollama junto a vLLM, Docker Model Runner, LocalAI y los compromisos de alojamiento en la nube, consulta Alojamiento de LLM en 2026: Comparativa de infraestructura local, autoalojada y en la nube.

Para ejemplos de solicitudes y código de cliente, consulta Ficha técnica de la CLI de Ollama.

Para capas de interfaz de usuario y multiusuario, consulta Visión general de Open WebUI, inicio rápido y alternativas.

Para la visión general sobre la autoalojamiento y el control de datos, consulta Autoalojamiento de LLM y soberanía de la IA.

Para un servicio de Ollama de nodo único reproducible en Docker Compose (volumenes persistentes, OLLAMA_HOST, GPUs de NVIDIA, actualizaciones), consulta Ollama en Docker Compose con GPU y almacenamiento persistente de modelos.

Para dispositivos remotos sin puertos de entrada públicos (Tailscale, WireGuard, enlace, firewall), consulta Acceso remoto a Ollama vía Tailscale o WireGuard, sin puertos públicos.

Por qué deberías usar un proxy para Ollama en lugar de exponer el puerto 11434

Ollama está diseñado para ejecutarse primero localmente. Por defecto, se vincula al localhost en el puerto 11434, lo cual es ideal para una estación de trabajo de desarrollo y una indicación no muy sutil de que el puerto en bruto no está destinado a salir a internet.

Trato el puerto 11434 como una API interna de alto coste. Si es accesible desde internet público, cualquiera que la encuentre puede quemar tu tiempo de CPU o GPU, llenar tu disco descargando modelos o simplemente mantener las conexiones abiertas hasta que algo caduque. Un proxy inverso no hace que Ollama sea más seguro por arte de magia, pero te ofrece un lugar para colocar los controles que importan en el borde: TLS, autenticación, tiempos de espera, límites de tasa y registros.

Esto importa porque la API local de Ollama no viene con una capa de autenticación integrada. Si la expones, normalmente añades la autenticación en el borde o la mantienes privada y accesible solo a través de una red de confianza.

El segundo motivo es la experiencia de usuario. Ollama transmite respuestas por defecto. Si el proxy almacena en búfer o comprime en el lugar equivocado, la transmisión parece rota y las interfaces de usuario parecen estar “pensando” sin mostrar salida.

Estrategia de vinculación y arquitectura mínima

Una arquitectura mínima limpia se ve así:

Cliente (curl, Python, Go, UI)
        |
        | HTTPS (Autenticación básica opcional o SSO)
        v
Proxy inverso (Caddy o Nginx)
        |
        | HTTP (LAN privada, localhost o red de Docker)
        v
Servidor Ollama (ollama serve en 127.0.0.1:11434)

Dos reglas prácticas mantienen esto aburrido de la mejor manera.

Primero, mantén Ollama privado y traslada la exposición al proxy. Si Caddy o Nginx se ejecutan en el mismo host, haz proxy a 127.0.0.1:11434 y no cambies la dirección de enlace de Ollama. Si el proxy se ejecuta en otro lugar (host separado, VM separada o red de contenedores), vincula Ollama a una interfaz privada, no a 0.0.0.0 en la NIC pública, y confía en un firewall.

Segundo, decide pronto si los navegadores llamarán a Ollama directamente. Si una herramienta basada en navegador golpea Ollama desde un origen diferente, es posible que tengas que lidiar con CORS. Si todo se sirve desde un dominio a través del proxy (recomendado para la cordura), a menudo puedes evitar CORS por completo y mantener a Ollama estricto.

Configuraciones de proxy inverso para transmisión y WebSockets

La API de Ollama es HTTP regular y su transmisión es JSON delimitado por saltos de línea (NDJSON). Eso significa que quieres un proxy que pueda hacer tres cosas bien:

  • No almacenar en búfer las respuestas de transmisión.
  • No matar solicitudes de larga duración solo porque el modelo tardó un tiempo en hablar.
  • Si una UI usa WebSockets (algunas lo hacen), reenvía el Upgrade limpiamente.

Puedes mantener esto simple. En muchos casos, el “manejo correcto de WebSockets” es simplemente tener una configuración que sea segura para Upgrade incluso si el upstream no usa WebSockets hoy en día.

Ejemplo de Caddyfile

Caddy es la opción de “menos configuración, más valores predeterminados”. Si pones un nombre de dominio público en la dirección del sitio, Caddy obtendrá y renovará los certificados automáticamente.

Configuración mínima de proxy inverso, HTTPS y ajustes favorables a la transmisión:

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

    # Autenticación básica opcional en el borde.
    # Genera un hash de contraseña con:
    #   caddy hash-password --algorithm bcrypt
    #
    # basic_auth {
    #   alice $2a$12$REDACTED...
    # }

    reverse_proxy 127.0.0.1:11434 {

        # Algunas configuraciones prefieren fijar el encabezado Host del upstream.
        # La propia documentación de Ollama muestra este patrón para Nginx.
        header_up Host localhost:11434

        # Para cargas de trabajo de transmisión o tipo chat, prefiere baja latencia.
        # La transmisión NDJSON generalmente vacía inmediatamente de todos modos, pero esto lo hace explícito.
        flush_interval -1

        transport http {
            # Evita la negociación de gzip upstream si interfiere con la transmisión.
            compression off

            # Da tiempo a Ollama para cargar un modelo y producir el primer fragmento.
            response_header_timeout 10m
            dial_timeout 10s
        }
    }
}

Si ya tienes una puerta de enlace SSO (oauth2-proxy, Authelia, outpost de authentik, etc.), Caddy tiene una directiva de autenticación directa opinionada. El patrón es “autenticación primero, luego proxy”:

ollama.example.com {
    forward_auth 127.0.0.1:4180 {
        uri /oauth2/auth
        # Copia los encabezados de identidad que devuelve tu puerta de enlace, si los necesitas.
        copy_headers X-Auth-Request-User X-Auth-Request-Email Authorization
    }

    reverse_proxy 127.0.0.1:11434
}

Ejemplo de bloque de servidor de Nginx

Nginx te ofrece un poco más de libertad. La ventaja es que los controles son explícitos y tiene primitivas integradas para limitar la tasa y limitar conexiones. El problema es el almacenamiento en búfer: Nginx almacena en búfer las respuestas proxificadas por defecto, lo cual es lo opuesto a lo que quieres para transmisión NDJSON.

Este ejemplo incluye:

  • Redirección de HTTP a HTTPS
  • Rutas de certificados TLS (estilo Certbot)
  • Reenvío seguro de Upgrade para WebSocket
  • proxy_buffering off favorable a la transmisión
  • Tiempos de espera más largos que el predeterminado de 60s
# /etc/nginx/conf.d/ollama.conf

# Manejo seguro de encabezados de conexión para WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

# Limitación de tasa de solicitudes opcional (basada en 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;

    # Autenticación básica opcional en el borde.
    # auth_basic "Ollama";
    # auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        # Limitación de tasa opcional
        # limit_req zone=ollama_rate burst=20 nodelay;

        proxy_pass http://127.0.0.1:11434;

        # Coincide con el patrón de la documentación de Ollama al hacer proxy a localhost.
        proxy_set_header Host localhost:11434;

        # Manejo de Upgrade de WebSocket (inofensivo si no se usa).
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Crítico para transmisión NDJSON.
        proxy_buffering off;

        # Evita tiempos de espera inactivos de 60s mientras se esperan tokens.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Si quieres una puerta de estilo SSO en Nginx, el patrón equivalente es auth_request. Nginx envía una sub-solicitud a tu servicio de autenticación y solo proxifica a Ollama cuando la autenticación devuelve 2xx.

Problemas de automatización y renovación de TLS

Para TLS, la división operativa es simple.

Con Caddy, TLS suele ser “parte del proxy inverso”. HTTPS automático es una de sus funciones insignia, por lo que la emisión y renovación de certificados están acopladas a mantener Caddy ejecutándose, tener DNS funcional y exponer los puertos 80 y 443.

Con Nginx, TLS suele ser “un cliente ACME separado más Nginx”. El modo de fallo común no es la criptografía, sino la tubería:

  • Puerto 80 no accesible para desafíos HTTP-01.
  • Certificados almacenados en un contenedor pero no persistidos.
  • Límites de tasa al realizar instalaciones frescas repetidas o despliegues de prueba.

Un punto sutil que importa para servicios de larga duración es que la vida útil de los certificados es corta por diseño. Trata las renovaciones como un requisito de automatización de fondo, no como un evento anual en el calendario.

Autenticación, control de abuso y verificación

Esta es la parte que hace que un endpoint de LLM orientado a internet se sienta profesional.

Opciones de autenticación, de rudas a elegantes

La autenticación básica en el proxy es ruda, pero sorprendentemente efectiva para un endpoint privado. También es fácil de aplicar tanto a solicitudes HTTP como a actualizaciones de WebSocket.

Si quieres flujos de inicio de sesión amigables con el navegador, la autenticación directa (forward auth) y auth_request son el patrón común. Tu proxy permanece sin estado y una puerta de enlace de autenticación posee las sesiones y MFA. El compromiso es tener más partes móviles.

Si ya estás ejecutando Open WebUI, también puedes confiar en su autenticación a nivel de aplicación y mantener Ollama mismo privado. El proxy entonces protege Open WebUI, no directamente a Ollama.

Si no necesitas acceso público en absoluto, un enfoque solo de red puede ser más limpio. Por ejemplo, Tailscale Serve puede exponer un servicio local dentro de tu tailnet sin abrir puertos de entrada en tu router. Para patrones completos (WireGuard, OLLAMA_HOST en interfaces VPN, fijación de firewall, lista de verificación de seguridad), consulta Acceso remoto a Ollama vía Tailscale o WireGuard, sin puertos públicos.

Básicos de abuso para una API costosa

Ollama es una potente API local y su superficie va más allá de la generación. Tiene endpoints para chat, incrustaciones, listado de modelos y verificaciones de versión. Trata toda la API como sensible.

Referencia oficial de la API (endpoints y transmisión): https://docs.ollama.com/api

En la capa del proxy, hay tres controles de bajo esfuerzo que reducen el dolor del primer día:

  • Limitación de tasa por IP en endpoints de generación.
  • Límites de conexión para detener que un pequeño número de clientes mantenga todo abierto.
  • Tiempos de espera conservadores que coincidan con tu realidad de modelo y hardware, no con valores predeterminados web genéricos.

En la capa de Ollama, también puede rechazar la sobrecarga con 503 y tiene controles del lado del servidor para colas. La limitación de tasa del proxy te evita llegar allí con tanta frecuencia.

Lista de verificación de verificación

Usa las mismas comprobaciones que usarías para cualquier API de transmisión.

  1. Conectividad básica y TLS

    • curl -sS https://ollama.example.com/api/version
    • curl -sS https://ollama.example.com/api/tags | head
  2. La transmisión funciona de extremo a extremo (sin almacenamiento en búfer)

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

    Si estás detrás de autenticación 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. Cordura de la interfaz de usuario del navegador

    • Carga tu interfaz de chat y desencadena una respuesta.
    • Si la UI usa WebSockets, confirma que no ves errores 400 o 426 y que la conexión se mantiene abierta durante la generación.

Si la salida de curl solo aparece al final, casi siempre es almacenamiento en búfer en el proxy. Vuelve a comprobar proxy_buffering off en Nginx y considera forzar el vaciado de baja latencia en Caddy para el bloque de sitio de Ollama.