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

Heroku部署在线剪刀石头布游戏出现404错误求助

Heroku部署后出现404错误(Cannot GET /)问题分析与解决

问题背景

本地开发的在线剪刀石头布游戏运行正常,但部署到Heroku后,控制台提示Cannot GET /及资源加载失败:服务器返回404状态码。Heroku日志显示访问根路径/时返回404。

服务器代码(index.js)

const express = require("express");
const app = express();
const http = require("http");
const { Server } = require("socket.io");
const socketio = require("socket.io");
const cors = require("cors");

app.use(cors());

const server = http.createServer(app);

let players = [];
let otherPlayerChoice = null;
let playerChoice = null;

// const io = new Server(server, {
//   cors: {
//     origin: "https://rock-paper-scissors-online.herokuapp.com",
//     methods: ["GET", "POST"],
//   },
// });

const io = socketio(server);

io.on("connection", (socket) => {
  console.log(`User Connected: ${socket.id}`);

  players.push(socket.id);

  socket.on("join-room", (number) => {
    socket.join(number);
  });

  const playerNumber = players.indexOf(socket.id);
  socket.emit("playerNumber", playerNumber);

  socket.on("disconnect", () => {
    console.log(`User Disconnected: ${socket.id}`);

    const index = players.indexOf(socket.id);
    players.splice(index, 1);
  });

  socket.on("choice", (data) => {
    console.log(`Player ${data.playerNumber} chose ${data.choice}`);

    // Find the other player
    const otherPlayer = players.find((player) => player !== socket.id);

    // Get the other player's choice
    socket.to(otherPlayer).emit("opposing-player-move", data.choice);
    socket.on("test", (move) => {
      otherPlayerChoice = move.choice;
      playerChoice = data.choice;

      // Determine the winner
      let result;

      if (playerChoice === otherPlayerChoice) {
        result = "It's a tie!";
      } else if (
        (playerChoice === 0 && otherPlayerChoice === 2) ||
        (playerChoice === 1 && otherPlayerChoice === 0) ||
        (playerChoice === 2 && otherPlayerChoice === 1)
      ) {
        result = `Player ${data.playerNumber === 0 ? 1 : 0} wins!`;
      } else {
        result = `Player 2 wins!`;
      }

      // Emit the result to both players
      io.to(move.room).emit("game-result", result);

      // io.to(otherPlayer).emit("game-result", result);
      io.emit("disable-buttons");
    });
  });
});

server.listen(process.env.PORT || 5000, () => {
  console.log("SERVER IS RUNNING");
});

Heroku日志

2023-04-12T17:05:08.265241+00:00 app[web.1]: SERVER IS RUNNING
2023-04-12T17:05:08.543766+00:00 heroku[web.1]: State changed from starting to up
2023-04-12T17:05:10.135071+00:00 heroku[router]: at=info method=GET path="/" host=rock-paper-scissors-online.herokuapp.com request_id=1ee5c19c-7db9-48bf-a0da-dc83b386c9b2 fwd="109.96.9.152" dyno=web.1 connect=0ms service=5ms status=404 bytes=415 protocol=https

问题原因

  1. 根路径未处理:你的Express服务器没有配置根路径(/)的响应逻辑,也没开启静态文件托管。本地运行时你可能直接打开前端HTML文件(file://协议),绕开了Express;但Heroku上所有请求都经过服务器,找不到对应路由就返回404。
  2. Socket.IO CORS配置缺失:注释掉的CORS配置会导致线上环境可能出现跨域问题,影响Socket连接。
  3. 全局玩家存储隐患:players数组是全局变量,Heroku多实例部署时会导致数据不共享,但这是后续优化问题,当前首要解决404。

解决步骤

1. 开启静态文件托管

假设前端文件(HTML/CSS/JS)放在项目根目录的public文件夹下,在app.use(cors());之后添加:

app.use(express.static("public"));

这样访问根路径时,Express会自动返回public/index.html(如果存在)。

2. 兜底根路由(可选)

如果需要自定义根路径响应,添加路由处理:

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/public/index.html");
});

3. 恢复Socket.IO CORS配置

取消注释Socket.IO的CORS配置,确保线上跨域正常:

const io = new Server(server, {
  cors: {
    origin: "https://rock-paper-scissors-online.herokuapp.com",
    methods: ["GET", "POST"],
  },
});

测试阶段可以用origin: "*"放宽限制,生产环境建议指定具体域名。

4. 确认项目配置

  • 确保public文件夹存在且包含前端文件
  • package.json的scripts里有正确启动命令:
    "scripts": {
      "start": "node index.js"
    }
    

内容的提问来源于stack exchange,提问作者Stefan-Yves

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:14:59