DigitalOcean上NodeJS+SocketIO应用本地访问CORS报错求助
我尝试从本地环境(http://localhost:3000)访问部署在DigitalOcean上的NodeJS应用,却持续触发CORS报错,具体错误如下:
Access to XMLHttpRequest at 'url.ondigitalocean.app/socket.io/?EIO=4&transport=polling&t=OFlEPJW' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
url.ondigitalocean.app/socket.io/?EIO=4&transport=polling&t=OFlEPJW:1 Failed to load resource: net::ERR_FAILED
其中https://admin.socket.io可正常访问,但本地连接被拒绝。以下是我的服务端代码、客户端代码及服务器配置:
服务端代码
const http = require("http"); const express = require("express"); const {instrument} = require('@socket.io/admin-ui') const {jwtDecode, jwtVerify, resignJwt} = require('jwt-js-decode') const secret = process.env.JWT_SECRET const app = express() const server = http.createServer(app) const io = require("socket.io")(server, { cors: { origin: [ "https://admin.socket.io", "http://localhost:3000/", "http://localhost:3000", "http://127.0.0.1:3000", "http://127.0.0.1:3000/", "http://localhost:3001", "http://localhost:3001/" ], credentials: true, }, });
客户端代码
import { io } from "socket.io-client"; const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN') const ioSocket = io('url.ondigitalocean.app', { withCredentials: true, transportOptions: { polling: { extraHeaders: { 'authorization': `${token}`, }, }, }, }); export const socket = ioSocket
服务器配置
name: dolphin-app region: fra services: - cors: allow_headers: - '*' allow_methods: - GET - OPTIONS - POST - PUT - PATCH - DELETE - HEAD - CONNECT - TRACE allow_origins: - exact: http://localhost:3000 - prefix: http://localhost:3000 - prefix: http://127.0.0.1:3000 - prefix: http://localhost environment_slug: node-js github: branch: main deploy_on_push: true repo: 1 http_port: 8080 instance_count: 1 instance_size_slug: basic-xs name: websocket routes: - path: / run_command: npm start source_dir: /
1. 客户端连接地址缺少HTTPS协议前缀
客户端代码中连接地址url.ondigitalocean.app未指定https://,浏览器会默认使用本地页面的HTTP协议发起请求,而DigitalOcean的服务会强制将HTTP请求重定向到HTTPS。但CORS预请求(OPTIONS)不允许重定向,直接触发报错。
修复方案:
修改客户端连接地址为完整HTTPS URL:
const ioSocket = io('https://url.ondigitalocean.app', { withCredentials: true, transportOptions: { polling: { extraHeaders: { 'authorization': `${token}`, }, }, }, });
2. 服务端CORS配置冗余
服务端Socket.IO的cors配置中同时包含带斜杠和不带斜杠的origin(如http://localhost:3000/和http://localhost:3000),属于重复配置,虽然Socket.IO能兼容,但会增加维护成本。另外,DigitalOcean服务级CORS配置与代码层配置可能存在冲突,建议统一在一处配置。
优化方案:
精简服务端origin列表,去掉重复项:
const io = require("socket.io")(server, { cors: { origin: [ "https://admin.socket.io", "http://localhost:3000", "http://127.0.0.1:3000", "http://localhost:3001" ], credentials: true, }, });
3. 验证服务监听端口匹配
确保服务端代码中监听的端口与DigitalOcean配置的http_port: 8080一致,若服务端未显式指定监听端口,需补充:
server.listen(8080, () => { console.log('Server running on port 8080'); });
内容的提问来源于stack exchange,提问作者Sasha

