Nginx configureren als reverse proxy
Configureer Nginx als reverse proxy voor Node.js, Ollama en andere backends. Behandelt proxy_pass, header forwarding, WebSocket-ondersteuning, upstream load balancing en productie-timeouts met verificatie bij elke stap.
Een reverse proxy staat tussen clients en je backend-applicatie. Nginx ontvangt binnenkomende verzoeken, stuurt ze door naar een backend-server (Node.js, Python, Go, Ollama) en geeft het antwoord terug aan de client. Hiermee kun je TLS-terminatie, load balancing, caching en toegangscontrole toevoegen zonder je applicatie aan te passen.
Deze tutorial behandelt Nginx reverse proxy configuratie, van een eenvoudige proxy_pass tot WebSocket proxying, upstream load balancing en een productieklare Ollama-setup.
Vereisten
Voordat je begint, heb je nodig:
- Een VPS met Debian 12 of Ubuntu 24.04 en root- of sudo-toegang
- Nginx geïnstalleerd en actief (Nginx installeren op Debian en Ubuntu)
- Een backend-applicatie die luistert op een lokale poort (de tutorial bevat testvoorbeelden)
- Basiskennis van de Nginx-configuratiestructuur (Nginx configuratiebestandsstructuur)
Controleer of Nginx draait:
sudo systemctl status nginx
Je zou active (running) in de output moeten zien.
Hoe configureer je proxy_pass in Nginx?
De proxy_pass directive vertelt Nginx waar verzoeken naartoe gestuurd moeten worden. Plaats het in een location-blok binnen een server-blok. Nginx stuurt het verzoek van de client naar de opgegeven backend-URL en streamt het antwoord terug. De directive accepteert HTTP- en HTTPS-URL's en kan verwijzen naar een IP-adres, domeinnaam of Unix-socket.
Maak een nieuw server block-bestand aan:
sudo nano /etc/nginx/sites-available/app.conf
Voeg een minimale reverse proxy configuratie toe:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Activeer de site en test de configuratie:
sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo nginx -t
Als nginx -t teruggeeft syntax is ok en test is successful, herlaad dan:
sudo systemctl reload nginx
Controleer of de proxy werkt:
curl -I http://app.example.com
De response headers moeten afkomstig zijn van je backend-applicatie. Als je een 502 Bad Gateway ziet, draait je backend niet op poort 3000.
Wat gebeurt er met een trailing slash in proxy_pass?
De trailing slash in proxy_pass bepaalt hoe URI's herschreven worden. Dit is een van de meest voorkomende configuratiefouten.
| Configuratie | Verzoek | Doorgestuurd naar backend |
|---|---|---|
proxy_pass http://127.0.0.1:3000 |
/app/users |
http://127.0.0.1:3000/app/users |
proxy_pass http://127.0.0.1:3000/ |
/app/users |
http://127.0.0.1:3000/users |
proxy_pass http://127.0.0.1:3000/v2/ |
/app/users |
http://127.0.0.1:3000/v2/users |
proxy_pass http://127.0.0.1:3000/v2 |
/app/users |
http://127.0.0.1:3000/v2users |
De regel: wanneer proxy_pass een URI bevat (alles na host:port, zelfs alleen /), verwijdert Nginx het overeenkomende location-prefix uit de verzoek-URI en voegt de rest toe aan de proxy_pass-URI. Wanneer proxy_pass geen URI heeft, wordt het oorspronkelijke pad ongewijzigd doorgestuurd.
Voor een location /app/-blok gelden bovenstaande voorbeelden. Let op de vierde rij: zonder trailing slash na /v2 wordt het pad /v2users in plaats van /v2/users. Voeg altijd een trailing slash toe wanneer je een pad specificeert.
Welke headers moet je doorsturen naar de backend?
Zonder expliciete headerconfiguratie kan je backend-applicatie het echte IP-adres van de client, het oorspronkelijke protocol of de gevraagde hostnaam niet zien. Nginx vervangt deze standaard. Stuur ze handmatig door.
| Header | Waarde | Doel |
|---|---|---|
Host |
$host |
Behoudt de originele Host-header zodat de backend weet welk domein is opgevraagd |
X-Real-IP |
$remote_addr |
Geeft het IP-adres van de client door aan de backend |
X-Forwarded-For |
$proxy_add_x_forwarded_for |
Voegt het client-IP toe aan de keten van proxy's |
X-Forwarded-Proto |
$scheme |
Vertelt de backend of het oorspronkelijke verzoek HTTP of HTTPS gebruikte |
Voeg deze headers toe aan je location-blok:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Na het herladen van Nginx, controleer of de headers de backend bereiken. Als je app binnenkomende headers logt, bekijk ze:
sudo systemctl reload nginx
curl -s http://app.example.com/headers
Je backend zou X-Real-IP moeten tonen met het IP van de client, niet 127.0.0.1. Als het 127.0.0.1 toont, ontbreken de proxy_set_header directives of staan ze in de verkeerde context.
Beveiligingsopmerking: gebruik $proxy_add_x_forwarded_for in plaats van $remote_addr voor X-Forwarded-For. Het voegt het client-IP toe aan een bestaande X-Forwarded-For header. Als je alleen $remote_addr instelt, verlies je de proxy-keten, wat IP-tracking achter meerdere proxy's verbreekt. Als Nginx je enige proxy is, zijn ze gelijkwaardig.
Hoe proxy je een Node.js-applicatie met een reverse proxy?
Hier is een volledig server block voor het proxyen van een Node.js-applicatie op poort 3000. Dit voorbeeld bevat header forwarding, WebSocket-ondersteuning en het verbergen van de versie.
Maak het server block aan:
sudo nano /etc/nginx/sites-available/nodeapp.conf
server {
listen 80;
server_name nodeapp.example.com;
# Hide Nginx version in responses
server_tokens off;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Header forwarding
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
}
Activeer, test en herlaad:
sudo ln -s /etc/nginx/sites-available/nodeapp.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Controleer of de proxy werkt:
curl -I http://nodeapp.example.com
Let op: de response zou geen Server: nginx/1.x.x regel mogen bevatten. De server_tokens off directive verbergt het versienummer. Versie-informatie helpt aanvallers bekende kwetsbaarheden te targeten.
Zie Nginx SSL/TLS met Let's Encrypt voor TLS-terminatie met Let's Encrypt.
Hoe proxy je WebSocket-verbindingen met Nginx?
WebSocket-verbindingen beginnen als een HTTP-verzoek met een Upgrade header en schakelen dan over naar een persistente bidirectionele verbinding. Nginx gebruikt standaard HTTP/1.0 voor upstream-verbindingen, wat het Upgrade-mechanisme niet ondersteunt. Je hebt drie directives nodig om WebSocket te laten werken: stel proxy_http_version in op 1.1, stuur de Upgrade header door en zet de Connection header op "upgrade".
Voor locations die zowel regulier HTTP- als WebSocket-verkeer afhandelen, gebruik de map directive om de Connection header conditioneel in te stellen. Voeg dit toe aan het http-blok in /etc/nginx/nginx.conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
Verwijs vervolgens naar $connection_upgrade in je server block:
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Prevent idle timeout killing WebSocket connections
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
De standaard proxy_read_timeout is 60 seconden. Als er in dat venster geen data over de verbinding gaat, sluit Nginx deze. Stel een hogere waarde in voor WebSocket-verbindingen. Je applicatie moet WebSocket ping frames sturen met een interval korter dan deze timeout.
Test de configuratie:
sudo nginx -t && sudo systemctl reload nginx
Om WebSocket-connectiviteit te verifiëren, installeer wscat en maak verbinding:
npm install -g wscat
wscat -c ws://ws.example.com/
Als de verbinding opent, werkt WebSocket proxying. Als je unexpected server response (200) krijgt, worden de Upgrade headers niet doorgestuurd. Controleer of de map directive in het http-blok staat, niet in een server-blok.
Hoe maak je een reverse proxy voor Ollama voor self-hosted AI?
Ollama serveert LLM-inferentie standaard op poort 11434 en bindt aan 127.0.0.1. Het proxyen via Nginx laat je TLS, authenticatie en toegangscontrole toevoegen zonder de Ollama-configuratie aan te passen. Het belangrijkste verschil met standaard proxying: LLM-inferentie kan minuten duren en streaming responses vereisen dat buffering uitgeschakeld is.
Maak het server block aan:
sudo nano /etc/nginx/sites-available/ollama.conf
server {
listen 80;
server_name ollama.example.com;
server_tokens off;
# Restrict access to specific IPs
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
location / {
proxy_pass http://127.0.0.1:11434;
proxy_http_version 1.1;
# Header forwarding
proxy_set_header Host localhost:11434;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection '';
# Disable buffering for streaming responses
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
# Extended timeouts for LLM inference
proxy_connect_timeout 300s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
Activeer en test:
sudo ln -s /etc/nginx/sites-available/ollama.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Controleer of Ollama reageert via de proxy:
curl http://ollama.example.com/api/tags
Je zou een JSON-response moeten zien met beschikbare modellen. Als je een 403 Forbidden krijgt, staat het IP van je client niet in de allow-lijst. Als je een 502 Bad Gateway krijgt, draait Ollama niet:
sudo systemctl status ollama
Test een streaming completion:
curl -N http://ollama.example.com/api/generate -d '{
"model": "llama3.2",
"prompt": "Hello",
"stream": true
}'
Let op: de -N flag schakelt curl's output buffering uit. Je zou tokens een voor een moeten zien binnenkomen. Als de volledige response in een keer aankomt, is proxy_buffering off niet ingesteld of wordt het overschreven.
Waarom deze instellingen afwijken van een standaard proxy:
proxy_buffering off: Nginx buffert normaal backend-responses en verstuurt ze als batch. Voor LLM streaming wil je dat elke token direct naar de client wordt gestuurd.proxy_read_timeout 600s: LLM-inferentie op grote modellen kan enkele minuten duren. De standaard timeout van 60s zou de verbinding halverwege de generatie verbreken.proxy_set_header Host localhost:11434: Ollama controleert de Host header en weigert verzoeken die niet overeenkomen met het geconfigureerde bind-adres.proxy_set_header Connection '': Wist de Connection header om keep-alive problemen met chunked streaming te voorkomen.
Zie Nginx SSL/TLS met Let's Encrypt voor TLS-terminatie om Ollama veilig via het internet beschikbaar te maken. Stel Ollama nooit bloot zonder toegangscontrole. Combineer IP-restricties met HTTP Basic Auth of API-key validatie voor productiegebruik.
Om HTTP Basic Auth toe te voegen bovenop IP-restricties:
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.ollama_htpasswd apiuser
sudo chmod 640 /etc/nginx/.ollama_htpasswd
sudo chown root:www-data /etc/nginx/.ollama_htpasswd
Voeg vervolgens toe aan het location-blok in ollama.conf:
auth_basic "Ollama API";
auth_basic_user_file /etc/nginx/.ollama_htpasswd;
Controleer of de bestandspermissies correct zijn:
ls -la /etc/nginx/.ollama_htpasswd
Het bestand moet -rw-r----- tonen met root als eigenaar en www-data als groep. Het beperken van permissies voorkomt dat andere gebruikers op de server de wachtwoordhashes kunnen lezen.
Hoe configureer je upstream load balancing?
Het upstream-blok definieert een groep backend-servers waarover Nginx verzoeken verdeelt. Standaard gebruikt Nginx gewogen round-robin. Elke server krijgt een deel van de verzoeken proportioneel aan zijn gewicht (standaardgewicht: 1).
Voeg een upstream-blok toe voor je server-blok:
upstream app_backends {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backends;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Load balancing methoden
Round-robin (standaard): verzoeken worden gelijkmatig over servers verdeeld. Geen directive nodig.
Least connections (minste verbindingen): stuurt verzoeken naar de server met de minste actieve verbindingen. Beter voor backends met wisselende responstijden:
upstream app_backends {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
IP hash: koppelt elk client-IP aan een specifieke backend. Handig voor sessiepersistentie zonder sticky cookies:
upstream app_backends {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
Health checks met max_fails en fail_timeout
Nginx monitort passief de gezondheid van backends. Als een server niet reageert, markeert Nginx deze als onbeschikbaar en stopt met het doorsturen van verzoeken voor een bepaalde periode.
upstream app_backends {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3002 backup;
}
| Parameter | Standaard | Beschrijving |
|---|---|---|
max_fails |
1 | Aantal mislukte pogingen binnen fail_timeout voordat de server als onbeschikbaar wordt gemarkeerd |
fail_timeout |
10s | Venster voor het tellen van fouten, en hoe lang de server onbeschikbaar blijft |
backup |
- | Server ontvangt alleen verzoeken wanneer alle primaire servers uitgevallen zijn |
weight |
1 | Relatief aandeel van verzoeken bij round-robin |
Na het configureren van upstream, test en herlaad:
sudo nginx -t && sudo systemctl reload nginx
Controleer of load balancing werkt door meerdere verzoeken te sturen en te kijken welke backend reageert:
for i in $(seq 1 6); do curl -s http://app.example.com/health; echo; done
Als je backends een identifier in hun response meegeven, zou je verzoeken verdeeld over de backends moeten zien.
Hoe stem je proxy buffering en timeouts af?
Nginx buffert standaard backend-responses. Het leest de volledige response van de backend in het geheugen (of op schijf als het de buffer overschrijdt) en stuurt het daarna naar de client. Dit is efficiënt voor de meeste applicaties, maar verkeerd voor streaming, Server-Sent Events (SSE) of long-polling.
Timeout directives
| Directive | Standaard | Aanbevolen | Doel |
|---|---|---|---|
proxy_connect_timeout |
60s | 5-10s | Tijd om verbinding met de backend op te bouwen. Houd kort om snel te falen. |
proxy_read_timeout |
60s | 60-300s | Tijd om op de backend-response te wachten. Verhoog voor trage API's. |
proxy_send_timeout |
60s | 60s | Tijd om de request body naar de backend te sturen. Verhoog voor grote uploads. |
location / {
proxy_pass http://127.0.0.1:3000;
proxy_connect_timeout 10s;
proxy_read_timeout 120s;
proxy_send_timeout 60s;
}
Deze timeouts gelden tussen twee opeenvolgende lees-/schrijfbewerkingen, niet voor het volledige verzoek. Een response die elke 30 seconden data stuurt, triggert nooit een proxy_read_timeout van 60 seconden.
Bufferinginstellingen
| Directive | Standaard | Beschrijving |
|---|---|---|
proxy_buffering |
on | Buffer de volledige backend-response voordat deze naar de client wordt gestuurd |
proxy_buffer_size |
4k of 8k | Buffer voor het eerste deel van de response (headers) |
proxy_buffers |
8 4k of 8 8k | Aantal en grootte van buffers voor de response body |
proxy_busy_buffers_size |
8k of 16k | Maximale grootte van buffers die bezig zijn met verzenden naar de client |
Wanneer moet je proxy buffering uitschakelen?
Schakel buffering uit wanneer de backend data streamt: LLM-inferentie (Ollama, vLLM), Server-Sent Events, WebSocket-achtige lange responses, of elke API die chunked data incrementeel verstuurt.
location /stream/ {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
}
Houd buffering ingeschakeld voor standaard request/response API's. Buffering beschermt je backend tegen trage clients: Nginx absorbeert de response snel en stuurt het naar de client op het tempo van de client. Zonder buffering houdt een trage client een backend-verbinding open.
Zie voor geavanceerde performance-tuning.
Hoe los je 502- en 504-fouten op?
502 Bad Gateway betekent dat Nginx geen verbinding kon maken met de backend of dat de backend een ongeldig antwoord stuurde. 504 Gateway Timeout betekent dat de backend niet binnen proxy_read_timeout reageerde.
502 Bad Gateway checklist
- Draait de backend?
ss -tlnp | grep 3000
Als er geen output is, luistert je backend niet op poort 3000. Start deze op.
-
Is de proxy_pass URL correct? Controleer op typfouten in het poortnummer of IP-adres. Veelgemaakte fout:
https://gebruiken voor een backend die alleen HTTP spreekt. -
Blokkeert SELinux de verbinding? (RHEL/CentOS)
sudo setsebool -P httpd_can_network_connect 1
- Controleer het Nginx error log:
sudo tail -20 /var/log/nginx/error.log
Zoek naar connect() failed (111: Connection refused) of no live upstreams.
504 Gateway Timeout checklist
- Verhoog proxy_read_timeout:
proxy_read_timeout 300s;
- Controleer of de backend traag is:
time curl http://127.0.0.1:3000/slow-endpoint
Als dit langer duurt dan je proxy_read_timeout, verhoog de timeout of optimaliseer de backend.
- Controleer de upstream-status: als je upstream blocks gebruikt, kunnen alle servers als gefaald zijn gemarkeerd. Controleer het error log op
no live upstreams while connecting to upstream.
Nginx logs lezen
Nginx schrijft foutdetails naar /var/log/nginx/error.log. Voor real-time monitoring:
sudo journalctl -u nginx -f
Of bekijk het error log direct:
sudo tail -f /var/log/nginx/error.log
Voeg voor per-site logs de directives access_log en error_log toe aan je server block:
server {
listen 80;
server_name app.example.com;
access_log /var/log/nginx/app-access.log;
error_log /var/log/nginx/app-error.log;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
Dit scheidt logs per applicatie, waardoor het makkelijker wordt om problemen met een specifieke backend te debuggen.
Veelvoorkomende headerproblemen
Als je backend 127.0.0.1 ontvangt als client-IP in plaats van het echte adres, ontbreekt de proxy_set_header X-Real-IP $remote_addr directive. Als je applicatie HTTP-links genereert met http:// terwijl het https:// zou moeten zijn, wordt de X-Forwarded-Proto header niet doorgestuurd. Sommige frameworks (Express, Django, Rails) vereisen expliciete configuratie om proxy headers te vertrouwen. In Express:
app.set('trust proxy', 1);
In Django, stel SECURE_PROXY_SSL_HEADER in:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Zonder deze instellingen negeert je applicatie de doorgestuurde headers, zelfs wanneer Nginx ze correct verstuurt.
Volledige referentie: proxy directives
Ter referentie, hier is de minimale set directives voor verschillende proxy-scenario's:
# Standard HTTP proxy
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket proxy
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
# Streaming proxy (SSE, LLM)
location /stream/ {
proxy_pass http://127.0.0.1:11434;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 600s;
}
Zie Nginx server blocks voor server blocks die meerdere domeinen beheren. Zie voor beveiligingshardening inclusief rate limiting en toegangscontrole.
Copyright 2026 Virtua.Cloud. Alle rechten voorbehouden. Deze inhoud is een origineel werk van het Virtua.Cloud-team. Reproductie, herpublicatie of herdistributie zonder schriftelijke toestemming is verboden.
Klaar om het zelf te proberen?
Deploy uw eigen server in seconden. Linux, Windows of FreeBSD.
Bekijk VPS-aanbod