Socket.io in production returns 400 Bad Request error

Viewed 1539

I am using Socket.IO in my application. The React client uses socket.io-client 4.1.3, and the Node.js server uses socket.io 4.1.3

In the development environment on my local machine, everything works fine.

The React app runs on http://localhost:3000, and connects to the server using:

import io from 'socket.io-client';
const socket = io('http://localhost:5000/');

The Node.js server is configured as below:

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const cors = require('cors');
const io = require('socket.io')(server, {
    cors: {
        origin: 'http://localhost:3000'
    },
    maxHttpBufferSize: '1e6'
});
app.set('io', io);
app.use(express.static('public'));
app.use(express.json({ limit: '7mb' }));
app.use(cors({ origin: 'http://localhost:3000' }));
server.listen(5000, () => console.log('Server started'));

In production, I am using Firebase to host the React app, in a subdirectory (e.g. https://www.example.com/app/).

In production, http://localhost:5000/ and http://localhost:3000 in the code above have also been changed to https://app.example.com and https://www.example.com/app respectively.

My server uses Ubuntu 20.04, Nginx, and Let's Encrypt, with a server block set up as follows:

server {

    server_name app.example.com;

    location / {
        proxy_pass http://localhost:5000/;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/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 = app.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name app.example.com;
    return 404; # managed by Certbot
}

In Google Chrome, I was getting No 'Access-Control-Allow-Origin' header is present on the requested resource as an error. Changing the origin from https://www.example.com/app to * in the Node.js code fixed this.

However, now I am getting the following error in my browser:

POST https://app.example.com/socket.io/?EIO=4&transport=polling&t=NirW_WK&sid=PmhwTyHRXOV4jWOdAAAF 400 (Bad Request)

Why would this be?

Thanks

3 Answers

A few small changes to both the Node.js and Nginx should resolve your problem:

Node.js

First off, I'd recommend that you change this:

cors: {
  origin: 'http://localhost:3000'
},

to this (as specified here):

cors: {
  origin: 'http://localhost:3000',
  methods: ["GET", "POST"]
},

Nginx

Change this:

location / {
  proxy_pass http://localhost:5000/;
}

to this:

location / {
  add_header 'Access-Control-Allow-Origin' '*';
  add_header 'Access-Control-Allow-Credentials' 'true';
  add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
  proxy_pass http://localhost:5000/;
}

This post here can help give more information on CORS headers needed in Nginx reverse proxies

Try adding a variable port to access an environment variable. PORT should be set to https://app.example.com/

const port = process.env.PORT || 3000

And use it everywhere that local host 3000 was used in your backend code.

This should also help

const io = require("socket.io")(server, {
cors: {

origin: port,
methods: ["GET", "POST"],
allowedHeaders: ['Access-Control-Allow-Origin'],
credentials: false

} })

I was facing same issue and backend was on aws elasticbeanstalk, so we set Load Balancer, to handle multiple request calls and this error was fixed. So i think you need to check cloud base function for load balancing.

Related