nginx setup for wss:// keep getting 301 redirects

Michael Dausmann asked:

Can’t get wss:// (or ws://) working on my Digital Ocean, Ubuntu server using nginx, keep getting 301 redirect and no connection.

Websocket server: node + express + uws served on http://localhost:3000/chat (I have tested it by opening up 3000 in ufw and connecting directly with a ws://, works fine.)

OS: Ubuntu 16.04.3 x64

Here is my nginx config (I have tried many many variants and options this is the simplest, honestly it doesn’t seem to matter)

server {
    listen 443 ssl; # client_wss_port
    server_name www.example.org;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;


    location /chat/ {
        add_header locationischat 1; # this is a dummy header for debugging
        proxy_pass http://localhost:3000/chat/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Here is my nginx version

nginx -v
nginx version: nginx/1.10.3 (Ubuntu)

Here is my firewall status

ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
80                         ALLOW       Anywhere                  
443                        ALLOW       Anywhere                  
8888                       DENY        Anywhere                  
3000                       DENY        Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
80 (v6)                    ALLOW       Anywhere (v6)             
443 (v6)                   ALLOW       Anywhere (v6)             
8888 (v6)                  DENY        Anywhere (v6)             
3000 (v6)                  DENY        Anywhere (v6) 

Here is an example of request/response (using Smart Websocket Client plugin on Chrome)

Request URL: wss://www.example.org/chat
Request Method: GET
Status Code: 301 Moved Permanently
Connection: keep-alive
Content-Length: 194
Content-Type: text/html
Date: Wed, 23 May 2018 10:42:35 GMT
Location: https://www.example.org/chat/
locationischat: 1
Server: nginx/1.10.3 (Ubuntu)
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cache-Control: no-cache
Connection: Upgrade
Host: www.example.org
Origin: chrome-extension://omalebghpgejjiaoknljcfmglgbpocdp
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: hFvk0oEAzI5FLVd4W2fgoA==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

Here is the nginx access.log for the above request

49.195.190.75 - - [23/May/2018:22:39:16 +0000] "GET /chat HTTP/1.1" 301 194 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"

My answer:


Your location directive refers to /chat/, but the endpoint you are trying to use is /chat.

Because a location exists with the same path, with a / appended, nginx generates an internal redirect.

If /chat is the WebSocket endpoint, then you should be using location /chat.

You are probably missing some other necessary headers. Here’s a working example of things you should have:

    location /ws {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Proxy "";
            proxy_set_header Host $http_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;
            proxy_pass http://localhost:8080;
    }

View the full question and answer on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.