[SOLVED] WebSocket Connection Issues on Production Server (404 Error on /websocket)

Hi everyone,

I’m facing a persistent issue with WebSocket connections in my Meteor app deployed to a production server. The app and http/https part work fine, but I am also developing an app using React Native that needs to connect to the server. I use the Meteor React Native library, and use this to connect to the server:

Meteor.connect(`wss://mydomain.com/websocket`, {
      autoConnect: true,
      autoReconnect: true,
      AsyncStorage: {
        getItem: SecureStore.getItemAsync,
        setItem: SecureStore.setItemAsync,
        removeItem: SecureStore.deleteItemAsync,
      }
    })

This didn’t work, and here was when I discovered the problem.
Despite trying various configurations, I keep getting a 404 error when connecting to wss://mydomain.com/websocket through tools like wscat or curl. Here are the details of my setup and what I’ve tried so far:

Environment Details:

  • Meteor version: 2.12
  • Node.js version: 14.21.3
  • OS: Ubuntu 20.04
  • Deployment: Using Meteor’s meteor build command and deploying the bundle.
  • Reverse Proxy: NGINX

What Works:

  • Regular HTTP requests work perfectly, and the app loads fine on https://mydomain.com.
  • SockJS fallback seems to be working (seen in NGINX logs).

The Problem:

  • All WebSocket connections to wss://mydomain.com/websocket fail with a 404 error.
  • Direct WebSocket requests to the backend (http://127.0.0.1:10013/websocket) also return a 404 error.

NGINX Configuration:

server {

    server_name mydomain.com;

    location / {
        proxy_pass http://127.0.0.1:10013;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /websocket {
        proxy_pass http://127.0.0.1:10013/websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }

    listen 443 ssl;
    ssl_certificate /route/to/certificate/fullchain.pem; # managed by Certbot
    ssl_certificate_key /route/to/certificate/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = mydomain.com) {
        return 307 https://$host$request_uri;
    } # managed by Certbot

    server_name mydomain.com;
    listen 80;
    return 404; # managed by Certbot
}

What I’ve Tried:

  1. Ensured WebSockets are enabled in the environment variables:
{
    "DISABLE_WEBSOCKETS": false,
    "ROOT_URL": "https://mydomain.com",
    "PORT": 10013,
    "MONGO_URL": "mongodb://localhost:27017/myapp"
}

Reinstalled dependencies in the Meteor bundle:

cd programs/server
npm install --force

Verified NGINX configuration with nginx -t and restarted the service.
Tested WebSocket connectivity with:

curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" https://mydomain.com/websocket

Result: 404 Not Found

And:

wscat -c wss://mydomain.com/websocket

Result: 404 Not Found

Checked NGINX logs, which show 404 errors for /websocket.

Am I missing something in the NGINX configuration to properly forward WebSocket traffic?

No Meteor / pm2 logs appear for WebSocket connection attempts, which suggests the requests aren’t reaching Meteor.

I’d appreciate any guidance or suggestions. If more details are needed, I’m happy to provide them.

Thanks in advance!

Check your “webapp” things, there might be some thing handles that request and response with 404 code.

There’s no webapp thing handling that request, the only part in my code using webapp is a middleware that I use to log all the incoming requests.

Apparently, the problem was in the NGINX configuration file, specifically in this line:

proxy_set_header Connection 'upgrade';

single quotes can be conflicting in certain environments, so when I changed ‘upgrade’ to “upgrade”, the problem was solved.

2 Likes