You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署在Netlify的Next.js自定义服务器WebSocket无法连接求助

问题描述

我正尝试将搭载自定义服务器、使用WebSocket实现实时更新的Next.js应用部署至Netlify。开发环境下WebSocket可正常工作,但部署后无法建立连接。已尝试多种配置与排查手段仍未解决,恳请有类似经验的开发者提供解决方案,感谢支持。

前端Socket Hook代码

import { useEffect } from "react";
import io from 'socket.io-client';

// Custom hook to handle socket connection
const useSocket = (onConnect, onOrderUpdate, onProductUpdate) => {
    // useEffect hook to handle socket connection
    useEffect(() => {
        // create new socket connection
        const socket = io()

        // if onConnect function is passed as an argument, register it as a listener for the "connect" event
        if (onConnect) {
            socket.on("connect", onConnect);
        }
        // if onOrderUpdate function is passed as an argument, register it as a listener for the "order-update" event
        if (onOrderUpdate) {
            socket.on("order-update", onOrderUpdate);
        }
        // if onProductUpdate function is passed as an argument, register it as a listener for the "product-update" event
        if (onProductUpdate) {
            socket.on("product-update", onProductUpdate);
        }
        // return a cleanup function to disconnect the socket when the component using the hook unmounts
        // socket disconnet onUnmount if exists
        if (socket) return () => socket.disconnect();
    }, []);
};

export default useSocket;

后端自定义服务器代码

const express = require('express');
const next = require('next');
const { createServer } = require('http');
const bodyParser = require('body-parser');
const Order = require('./models/Order.js');
const Product = require('./models/Product.js');
const cookie = require('cookie');
var cors = require('cors');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

const PORT = process.env.PORT || 3000;

app.prepare().then(() => {
    const exp = express()
    exp.use(cors({
        origin: 'https://pizza-app3.netlify.app'
    }))
    exp.use(bodyParser.json())
    exp.use(bodyParser.urlencoded({ extended: true }));

    exp.use((req, res, next) => {
        req.cookies = cookie.parse(req.headers.cookie || '');
        next();
    });

    exp.use((req, res, next) => {
        res.header("Access-Control-Allow-Origin", "https://pizza-app3.netlify.app");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });


    const server = createServer(exp);
    const io = require('socket.io')(server);

    exp.all('*', (req, res) => {
        return handle(req, res);
    });

    io.on('connection', (socket) => {
        console.log('A user connected');

        // Watch for changes in the Order model and emit an 'order-update' event
        Order.watch().on('change', (data) => {
            socket.emit('order-update', data);
        })

        // Watch for changes in the Product model and emit a 'product-update' event
        Product.watch().on('change', (data) => {
            socket.emit('product-update', data);
        });

        socket.on('disconnect', () => {
            console.log('A user disconnected');
        });
    });

    server.listen(PORT, (err) => {
        if (err) throw err;
        console.log(`> Ready on http://localhost:${PORT}`);
    });
}).catch((err) => {
    console.error(err.stack);
    process.exit(1);
});

package.json配置

{
  "name": "food-website2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js",
    "lint": "next lint"
  },
  "dependencies": {
    "@next/font": "13.1.1",
    "@reduxjs/toolkit": "^1.9.1",
    "axios": "^1.2.3",
    "body-parser": "^1.20.1",
    "cookie": "^0.5.0",
    "cors": "^2.8.5",
    "cross-env": "^7.0.3",
    "dotenv": "^16.0.3",
    "eslint": "8.31.0",
    "eslint-config-next": "13.1.1",
    "express": "^4.18.2",
    "http": "^0.0.1-security",
    "mongoose": "^6.8.4",
    "next": "13.1.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-loader-spinner": "^5.3.4",
    "react-paystack": "^4.0.2",
    "react-redux": "^8.0.5",
    "react-toastify": "^9.1.1",
    "socket.io": "^3.1.2",
    "socket.io-client": "^3.1.3"
  }
}
解决方案

核心问题是Netlify不支持直接运行自定义Node.js/Express服务器,它的核心是静态托管+无服务器函数,无法承载长连接的WebSocket服务。需要调整架构:

1. 拆分架构:静态前端+独立WebSocket服务器

Netlify只托管Next.js的静态构建产物,WebSocket服务器部署到支持Node.js长连接的平台(比如Render、Heroku、Vercel Functions、DigitalOcean App Platform等)。

2. 修改前端Socket连接配置

明确指定生产环境的WebSocket服务器地址,同时修复依赖项缺失问题:

import { useEffect } from "react";
import io from 'socket.io-client';

const useSocket = (onConnect, onOrderUpdate, onProductUpdate) => {
    useEffect(() => {
        // 替换为你部署后的WebSocket服务器地址
        const socketUrl = process.env.NODE_ENV === 'production' 
            ? 'https://your-websocket-server-domain.com'
            : '';

        const socket = io(socketUrl, {
            transports: ['websocket', 'polling'], // 优先WebSocket,降级到轮询
            withCredentials: true // 如果需要携带Cookie
        });

        if (onConnect) socket.on("connect", onConnect);
        if (onOrderUpdate) socket.on("order-update", onOrderUpdate);
        if (onProductUpdate) socket.on("product-update", onProductUpdate);

        return () => socket.disconnect();
    }, [onConnect, onOrderUpdate, onProductUpdate]); // 添加依赖项,避免闭包导致的事件监听异常
};

export default useSocket;

3. 调整后端WebSocket服务器配置

确保跨域配置正确兼容Socket.IO的握手请求:

// ...
// 创建Socket.IO实例时配置CORS
const io = require('socket.io')(server, {
    cors: {
        origin: "https://pizza-app3.netlify.app",
        methods: ["GET", "POST"],
        credentials: true
    }
});
// ...

同时可以移除重复的CORS中间件,避免冲突。

4. Netlify部署设置

  • 保持package.json的build命令为next build,Netlify会自动构建并托管静态文件。
  • 在Netlify控制台的部署设置中,确认发布目录为.next(Pages Router)或.next/out(App Router,需要开启静态导出)。

5. 版本兼容检查

确保前端socket.io-client和后端socket.io版本一致(当前都是3.x,没问题),避免因版本差异导致连接失败。

内容的提问来源于stack exchange,提问作者daniel asakpa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:01:28