生产中的 Socket.io 返回 400 Bad Request 错误

Posted

技术标签:

【中文标题】生产中的 Socket.io 返回 400 Bad Request 错误【英文标题】:Socket.io in production returns 400 Bad Request error 【发布时间】:2021-08-11 14:06:24 【问题描述】:

我在我的应用程序中使用 Socket.IO。 React 客户端使用socket.io-client4.1.3,Node.js 服务器使用socket.io4.1.3

在我本地机器上的开发环境中,一切正常。

React 应用程序在 http://localhost:3000 上运行,并使用以下方式连接到服务器:

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

Node.js 服务器配置如下:

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'));

在生产中,我使用 Firebase 在子目录中托管 React 应用程序(例如 https://www.example.com/app/)。

在生产中,上面代码中的http://localhost:5000/和http://localhost:3000也分别改为https://app.example.com和https://www.example.com/app。

我的服务器使用 Ubuntu 20.04、nginx 和 Let's Encrypt,服务器块设置如下:

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

在 Google Chrome 中,我得到 No 'Access-Control-Allow-Origin' header is present on the requested resource 作为错误。在 Node.js 代码中将原点从 https://www.example.com/app 更改为 * 修复了这个问题。

但是,现在我的浏览器出现以下错误:

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

为什么会这样?

谢谢

【问题讨论】:

能否请您在 nginx 中启用日志并将您看到的错误粘贴到那里? 另外,400 错误的响应正文是什么? 1.当您删除 cors 策略时会发生什么 2. 您可以尝试使用 webscoket 而不是 http 即。 ws://domainname 3. 在生产中我们可以连接本地主机吗? 在使用 let's encrypt 时,我发现 400 个错误请求错误有所增加。我相信这是向期望 HTTPS 加密流量的端点发出的纯 HTTP 请求之间的不匹配。我不是 100% 确定这是问题所在,但也许那里有你的线索,我会尝试首先删除由 certbot 管理的第二个 server 部分,然后将重定向 301 的缓存从该块擦除到HTTPS 阻止。 【参考方案1】:

对 Node.js 和 Nginx 的一些小改动应该可以解决您的问题:

Node.js

首先,我建议您更改此设置:

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

对此(指定here):

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

Nginx

改变这个:

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

到这里:

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 这里可以帮助提供有关 Nginx 反向代理中所需的 CORS 标头的更多信息

【讨论】:

【参考方案2】:

尝试添加变量端口以访问环境变量。端口应设置为https://app.example.com/

const port = process.env.PORT || 3000

并在后端代码中使用本地主机 3000 的任何地方使用它。

这也应该有帮助

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

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

)

【讨论】:

以上是关于生产中的 Socket.io 返回 400 Bad Request 错误的主要内容,如果未能解决你的问题,请参考以下文章

远程服务器返回意外响应:(400) Bad Request

远程服务器返回意外响应:(400) Bad Request

HttpWebRequest-远程服务器返回错误:(400) Bad Request

Laravel 6 护照在错误的凭证上返回 400 Bad request

HTTPClient 返回 400 Bad Request 但 Postman 在 C# 中返回 201

Ajax 返回 400 Bad Request (xhr),admin-ajax.php 返回 '0'?