0
votes

I have an Nginx server block that proxies requests to a node.js server. This server serves both HTTP content and WS (websocket) content. Is it okay to add upgrade headers on requests that should NOT upgrade to websocket connections?

i.e. Using Nginx to proxy to a Node.js server that serves HTTP and WS, would it be good practice to use separate server blocks?

This is my Nginx server block currently:

server {

  listen 443 ssl;
  listen [::]:443;
  server_name api.mysite.com;

  ssl_certificate ...;
  ssl_certificate_key ...;
  ssl_dhparam ...;
  ssl_protocols ...;
  ssl_prefer_server_ciphers on;
  ssl_ciphers ...;

  location / {

    proxy_pass http://localhost:5000;
    proxy_buffering off;
    proxy_redirect off;
    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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header Access-Control-Allow-Origin *;
  }
}

It looks like I'm always adding Upgrade and Connection headers to requests being proxied to the Node.js server, even if I don't want to upgrade:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

The first line looks like it's setting the "Upgrade" header to $http_upgrade, passed from the request. I assume that if this header is NOT passed in the request then "Upgrade" will be set to null (or equivalent), which will have no effect. Is that correct?

2

2 Answers

1
votes

I've found that it works fine in practice as well, although at least one user has had an issue with it.

This line:

proxy_set_header Upgrade $http_upgrade;

Is actually doing what you want because $http_upgrade comes from the header sent by the client. So if the client doesn't request an upgrade, it doesn't get passed along.

For some reason there doesn't seem to be an equivalent $http_connection variable, but you can create one with map, which is the official solution:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    ...

    location /chat/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}
0
votes

Nothing happens. It's fine. I'm using the same nginx block to proxy to a graphql http endpoint, and a ws endpoint.