Proxy pentru WebSocket în Nginx

Configurează antetele Upgrade și Connection pe HTTP/1.1 pentru ca conexiunile WebSocket să treacă prin Nginx.

Sintaxă
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Ce face Proxy pentru WebSocket

O conexiune WebSocket începe ca o cerere HTTP/1.1 obișnuită care conține antetele Upgrade: websocket și Connection: Upgrade. Dacă serverul acceptă, răspunde cu codul 101 Switching Protocols, iar conexiunea TCP rămâne deschisă pentru mesaje în ambele sensuri.

Pentru că Upgrade și Connection sunt antete hop-by-hop (valabile doar pe o singură legătură), un reverse proxy nu le transmite automat mai departe. În plus, Nginx folosește implicit HTTP/1.0 către backend. De aceea, pentru WebSocket sunt obligatorii trei directive în location: proxy_http_version 1.1 și transmiterea explicită a celor două antete.

De asemenea, conexiunile WebSocket pot rămâne deschise minute sau ore fără trafic; cum proxy_read_timeout este implicit de 60s, conexiunea se va închide după un minut de liniște dacă nu mărești acest timeout sau dacă aplicația nu trimite periodic pachete ping/pong.

Opțiuni și argumente

Opțiune / Parametru Descriere
proxy_http_version 1.1 Obligatoriu: mecanismul Upgrade există doar în HTTP/1.1.
Upgrade $http_upgrade Transmite mai departe valoarea antetului Upgrade trimis de browser.
Connection $connection_upgrade Setează upgrade când există $http_upgrade și close (sau "") pentru cereri HTTP normale.
proxy_read_timeout Timpul maxim între două citiri succesive de la backend; crește-l (de exemplu 3600s) pentru conexiuni WebSocket de durată.

Exemple

Exemplul 1: Locație dedicată pentru WebSocket (/ws/ sau /socket.io/)

Cea mai simplă variantă când conexiunile WebSocket au o cale separată:

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;

    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host       $host;
    proxy_set_header X-Real-IP  $remote_addr;

    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

Exemplul 2: Același location pentru HTTP normal și WebSocket (cu map)

Blocul map din http setează Connection: upgrade doar când clientul cere într-adevăr un upgrade:

# În contextul http:
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# În contextul server:
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host       $host;
}

Greșeli frecvente

  • Uitarea proxy_http_version 1.1;: backend-ul primește cererea pe HTTP/1.0, ignoră Upgrade și răspunde cu 200 sau 400 în loc de 101 Switching Protocols.
  • Deconectarea exact la 60 de secunde de inactivitate: cauza este proxy_read_timeout lăsat la valoarea implicită de 60s.

Comenzi înrudite

  • proxy_pass: Trimite cererea către un server backend (Node.js, Python, Go, Docker) și returnează răspunsul clientului.
  • proxy_set_header: Setează sau modifică antetele HTTP trimise către backend (Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto).
  • upstream: Definește un grup de servere backend între care Nginx distribuie traficul (load balancing) și gestionează conexiunile keepalive.