Ollama za certyfikatem odwróconym (reverse proxy) z Caddy lub Nginx do strumieniowego dostępu HTTPS

HTTPS z Ollama bez przerywania strumieniowych odpowiedzi.

Page content

Uruchomienie Ollama za pośrednictwem proxy odwrotnego to najprostszy sposób na uzyskanie HTTPS, opcjonalnej kontroli dostępu oraz przewidywalnego zachowania strumieniowego.

Ten artykuł skupia się na ingressie Caddy i Nginx dla API Ollama, a nie na kodzie klienta.

ollama behind proxy

Jeśli już masz klientów Python lub Go komunikujących się z Ollama, ten post jest brakującym elementem: ingress i transport dla tego samego API.

Jeśli chcesz dowiedzieć się, jak Ollama wpasowuje się obok vLLM, Docker Model Runner, LocalAI oraz kompromisów związanych z hostingiem w chmurze, zobacz Hosting LLM w 2026: Porównanie rozwiązań lokalnych, self-hosted i chmurowych.

Jeśli potrzebujesz przykładów żądań i kodu klienta, zobacz Skrot klawiszowy Ollama CLI.

Jeśli interesują Cię warstwy UI i wieloużytkownikowe, zobacz Przegląd Open WebUI, szybki start i alternatywy.

Jeśli szukasz szerszego kontekstu dotyczącego self-hostingu i kontroli nad danymi, zobacz Self-hosting LLM i suwerenność AI.

Jeśli potrzebujesz powtarzalnej usługi Ollama na pojedynczym węźle w Docker Compose (trwałe woluminy, OLLAMA_HOST, karty GPU NVIDIA, aktualizacje), zobacz Ollama w Docker Compose z GPU i trwałym magazynowaniem modeli.

Jeśli potrzebujesz dostępu do zdalnych urządzeń bez publicznych portów przychodzących (Tailscale, WireGuard, wiązanie, firewall), zobacz Zdalny dostęp do Ollama przez Tailscale lub WireGuard bez publicznych portów.

Dlaczego warto proxyfować Ollama zamiast eksponować port 11434

Ollama jest zaprojektowany tak, aby działać w pierwszej kolejności lokalnie. Domyślnie łączy się z localhostem na porcie 11434, co jest świetne dla stacji roboczej dewelopera i stanowi niejednoznaczny sygnał, że surowy port nie jest przeznaczony do wystawienia do internetu.

Traktuję port 11434 jako wewnętrzne, kosztowne API. Jeśli jest on osiągalny z publicznego internetu, każdy, kto go znajdzie, może spalić czas procesora lub GPU, wypełnić dysk pobierając modele lub po prostu utrzymywać połączenia otwarte, aż do wystąpienia timeoutu. Proxy odwrotne nie czyni Ollama bezpieczniejszym sam z siebie, ale daje Ci miejsce na umieszczenie kluczowych kontroli na krawędzi: TLS, uwierzytelnianie, limity czasu, limity żądań i logi.

To ma znaczenie, ponieważ lokalne API Ollama nie posiada wbudowanej warstwy uwierzytelniania. Jeśli je wystawisz, zazwyczaj dodajesz uwierzytelnianie na krawędzi lub utrzymujesz je prywatne i dostępne tylko przez zaufaną sieć.

Drugi powód to UX. Ollama domyślnie strumieniuje odpowiedzi. Jeśli proxy buforuje lub kompresuje w niewłaściwym miejscu, strumieniowanie wydaje się zepsute, a interfejsy użytkownika wyglądają, jakby “myślały” bez generowania outputu.

Minimalna architektura i strategia wiązania

Czysty minimum wygląda tak:

Klient (curl, Python, Go, UI)
        |
        | HTTPS (opcjonalnie Basic Auth lub SSO)
        v
Proxy odwrotne (Caddy lub Nginx)
        |
        | HTTP (prywatna LAN, localhost lub sieć Docker)
        v
Serwer Ollama (ollama serve na 127.0.0.1:11434)

Dwie praktyczne zasady utrzymują to nudne w najlepszym możliwym znaczeniu tego słowa.

Po pierwsze, utrzymuj Ollama w prywatności i przenoś ekspozycję do proxy. Jeśli Caddy lub Nginx działają na tym samym hoście, proxyfuj do 127.0.0.1:11434 i nie zmieniaj adresu wiązania Ollama. Jeśli proxy działa gdzie indziej (osobny host, osobna maszyna wirtualna lub sieć kontenerowa), wiąż Ollama z prywatnym interfejsem, a nie z 0.0.0.0 na publicznej karcie sieciowej, i polegaj na firewallu.

Po drugie, zdecyduj wcześnie, czy przeglądarki będą wywoływać Ollama bezpośrednio. Jeśli narzędzie oparte na przeglądarce uderza w Ollama z innej domeny, możesz musieć zmierzyć się z CORS. Jeśli wszystko jest serwowane z jednej domeny przez proxy (zalecane dla zachowania zdrowych zmysłów), często możesz całkowicie uniknąć CORS i zachować Ollama rygorystyczne.

Konfiguracje proxy odwrotnego dla strumieniowania i WebSockets

API Ollama to zwykłe HTTP, a jego strumieniowanie to JSON rozdzielany znakami nowej linii (NDJSON). Oznacza to, że potrzebujesz proxy, które potrafi dobrze robić trzy rzeczy:

  • Nie buforować odpowiedzi strumieniowych.
  • Nie przerywać długich żądań tylko dlatego, że model potrzebował więcej czasu na wypowiedź.
  • Jeśli UI używa WebSockets (niektóre tak), przekazywać Upgrade w sposób czysty.

Możesz to utrzymać w prostocie. W wielu przypadkach “poprawne obsłużenie WebSockets” to po prostu posiadanie konfiguracji bezpiecznej dla Upgrade, nawet jeśli upstream nie używa WebSockets dzisiaj.

Przykład Caddyfile dla Caddy

Caddy to opcja “mniej konfiguracji, więcej domyślnych ustawień”. Jeśli umieścisz publiczną nazwę domeny w adresie strony, Caddy zazwyczaj uzyska i odnowi certyfikaty automatycznie.

Minimalne ustawienia proxy odwrotnego, HTTPS i przyjazne dla strumieniowania:

# ollama.example.com A/AAAA -> twój host proxy
ollama.example.com {

    # Opcjonalne Basic Auth na krawędzi.
    # Wygeneruj hash hasła używając:
    #   caddy hash-password --algorithm bcrypt
    #
    # basic_auth {
    #   alice $2a$12$REDACTED...
    # }

    reverse_proxy 127.0.0.1:11434 {

        # Niektóre konfiguracje preferują pinowanie nagłówka Host upstream.
        # Dokumentacja Ollama pokazuje ten wzór dla Nginx.
        header_up Host localhost:11434

        # Dla obciążeń strumieniowych lub czatowych, preferuj niskie opóźnienia.
        # Strumieniowanie NDJSON zazwyczaj czyści natychmiastowo, ale to czyni to jawnym.
        flush_interval -1

        transport http {
            # Unikaj negocjacji gzip upstream jeśli to kłóci się ze strumieniowaniem.
            compression off

            # Daj Ollama czas na załadowanie modelu i wygenerowanie pierwszego kawałka.
            response_header_timeout 10m
            dial_timeout 10s
        }
    }
}

Jeśli już masz bramkę SSO (oauth2-proxy, Authelia, authentik outpost, itd.), Caddy ma opiniowany dyrektywę forward auth. Wzorem jest “najpierw auth, potem proxy”:

ollama.example.com {
    forward_auth 127.0.0.1:4180 {
        uri /oauth2/auth
        # Skopiuj nagłówki tożsamości zwracane przez twoją bramkę, jeśli ich potrzebujesz.
        copy_headers X-Auth-Request-User X-Auth-Request-Email Authorization
    }

    reverse_proxy 127.0.0.1:11434
}

Przykład bloku serwera Nginx

Nginx daje Ci nieco więcej swobody. Zaletą jest to, że pokrętła są jawne i ma wbudowane primitive dla limitowania żądań i połączeń. Pułapką jest buforowanie: Nginx domyślnie buforuje proxyfowane odpowiedzi, co jest przeciwieństwem tego, czego chcesz dla strumieniowania NDJSON.

Ten przykład zawiera:

  • Przekierowanie HTTP do HTTPS
  • Ścieżki do certyfikatów TLS (styl Certbot)
  • Bezpieczne dla WebSockets przekazywanie Upgrade
  • Przyjazne dla strumieniowania wyłączenie proxy_buffering
  • Dłuższe timeouty niż domyślne 60s
# /etc/nginx/conf.d/ollama.conf

# Bezpieczne dla WebSockets obsłużenie nagłówka Connection
map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

# Opcjonalne limitowanie żądań (na bazie 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;

    # Opcjonalne Basic Auth na krawędzi.
    # auth_basic "Ollama";
    # auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        # Opcjonalny limit żądań
        # limit_req zone=ollama_rate burst=20 nodelay;

        proxy_pass http://127.0.0.1:11434;

        # Dopasuj wzór z dokumentacji Ollama przy proxy do localhost.
        proxy_set_header Host localhost:11434;

        # Obsługa Upgrade WebSockets (bezwzględna jeśli nieużywana).
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Krytyczne dla strumieniowania NDJSON.
        proxy_buffering off;

        # Zapobiegaj timeoutom 60s podczas czekania na tokeny.
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Jeśli chcesz bramkę w stylu SSO w Nginx, odpowiednik to auth_request. Nginx wysyła podżądanie do twojej usługi auth i proxyfuje do Ollama tylko wtedy, gdy auth zwraca 2xx.

Pułapki automatyzacji i odnawiania TLS

Dla TLS podział operacyjny jest prosty.

Z Caddy, TLS jest zazwyczaj “częścią proxy odwrotnego”. Automatyczne HTTPS to jedna z jego flagowych funkcji, więc wydawanie certyfikatów i ich odnawianie jest powiązane z utrzymaniem Caddy w ruchu, działającym DNS i ekspozycją portów 80 i 443.

Z Nginx, TLS jest zazwyczaj “osobnym klientem ACME plus Nginx”. Wspólnym trybem awarii nie jest kryptografia, ale instalacja:

  • Port 80 nieosiągalny dla wyzwań HTTP-01.
  • Certyfikaty przechowywane w kontenerze, ale nie utrzymywane.
  • Limity żądań przy powtarzanych świeżych instalacjach lub testowych wdrożeniach.

Subtelny punkt, który ma znaczenie dla usług o długim cyklu życia, to to, że żywotność certyfikatów jest krótka przez projekt. Traktuj odnawiania jako wymaganie tła automatyzacji, a nie roczne wydarzenie kalendarzowe.

Uwierzytelnianie, kontrola nadużyć i weryfikacja

To jest ta część, która sprawia, że endpoint LLM skierowany do internetu wydaje się profesjonalny.

Opcje uwierzytelniania, od prostej do eleganckiej

Basic Auth na proxy jest proste, ale zaskakująco skuteczne dla prywatnego endpointu. Jest również łatwe do zastosowania zarówno do żądań HTTP, jak i upgrade’ów WebSocket.

Jeśli chcesz przepływy logowania przyjazne dla przeglądarki, forward auth i auth_request to powszechny wzór. Twoje proxy pozostaje bezstanowe, a bramka auth zarządza sesjami i MFA. Kompromisem jest więcej ruchomych części.

Jeśli już uruchamiasz Open WebUI, możesz również polegać na jego uwierzytelnieniu na poziomie aplikacji i utrzymać samo Ollama prywatne. Proxy wtedy chroni Open WebUI, a nie Ollama bezpośrednio.

Jeśli w ogóle nie potrzebujesz dostępu publicznego, podejście tylko sieciowe może być czystsze. Na przykład, Tailscale Serve może wystawić lokalną usługę wewnątrz twojego tailnet bez otwierania portów przychodzących na twoim routerze. Dla pełnych wzorców (WireGuard, OLLAMA_HOST na interfejsach VPN, pinowanie firewalla, lista bezpieczeństwa), zobacz Zdalny dostęp do Ollama przez Tailscale lub WireGuard bez publicznych portów.

Podstawy walki z nadużyciami dla kosztownego API

Ollama to potężne lokalne API, a jego powierzchnia wykracza poza generowanie. Ma endpointy do czatu, embeddingów, listy modeli i sprawdzania wersji. Traktuj całe API jako wrażliwe.

Oficjalna referencja API (endpointy i strumieniowanie): https://docs.ollama.com/api

Na warstwie proxy istnieją trzy niskokosztowe kontrole, które zmniejszają ból pierwszego dnia:

  • Limitowanie żądań na IP dla endpointów generowania.
  • Limity połączeń, aby zapobiec małej liczbie klientów utrzymujących wszystko otwarte.
  • Konserwatywne timeouty dopasowane do twojego modelu i rzeczywistości sprzętowej, a nie domyślnych ustawień webowych.

Na warstwie Ollama, może ono również odrzucić przeciążenie z kodem 503 i ma pokrętła serwera dla kolejki. Limitowanie żądań na proxy utrzymuje Cię z dala od tego tak często.

Lista kontrolna weryfikacji

Używaj tych samych sprawdzeń, które użyłbyś dla dowolnego strumieniowego API.

  1. Podstawowa łączność i TLS

    • curl -sS https://ollama.example.com/api/version
    • curl -sS https://ollama.example.com/api/tags | head
  2. Strumieniowanie działa od początku do końca (bez buforowania)

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

    Jeśli jesteś za 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}'
  3. Sensowność UI przeglądarkowego

    • Załaduj swój UI czatu i wywołaj odpowiedź.
    • Jeśli UI używa WebSockets, potwierdź, że nie widzisz błędów 400 lub 426 i połączenie pozostaje otwarte podczas generowania.

Jeśli output curla pojawia się tylko na końcu, prawie zawsze jest to buforowanie na proxy. Ponownie sprawdź proxy_buffering off w Nginx i rozważ wymuszenie niskiego opóźnienia czyszczenia w Caddy dla bloku strony Ollama.