MERN应用部署Heroku后服务器请求被拒绝问题求助
MERN应用部署Heroku后前端接口调用CONNECTION REFUSED问题排查与解决
问题现象
MERN应用已部署至Heroku,Postman可正常访问后端接口并获取正确返回,但生产环境下前端调用接口时提示CONNECTION REFUSED,请求无法完成。
相关代码片段
前端Services文件(目标接口带/api前缀)
import http from "../utils/http-common"; class CountriesService { getAll() { return http.get("/api/country"); } getAllCountries() { return http.get("/country/getAll"); } getScroll(skip) { return http.get(`/country?skip=${skip}`); } get(id) { return http.get(`/country/${id}`); } create(countryForm) { return http.post("/country/new", countryForm); } edit(id, values) { return http.put(`/country/${id}`, values); } delete(id) { return http.delete(`/country/${id}`); } } export const getPostsPage = async (pageParam = 1, options = {}) => { const response = await http.get(`/api/country?_page=${pageParam}`, options) return response.data } export default new CountriesService();
前端http-common配置
import axios from "axios"; var url; process.env.REACT_APP_NODE_ENV === "production" ? (url = "") : (url = "http://localhost:5000/"); export default axios.create({ baseURL: `${url}`, headers: { 'Content-Type': 'application/json', }, withCredentials: true, });
后端Server配置
const path = require('path'); require("dotenv").config(); const express = require("express"); const app = express(); const mongoose = require("mongoose"); const cors = require("cors"); var morgan = require("morgan"); const MongoDBStore = require('connect-mongo'); const mongoSanitize = require('express-mongo-sanitize'); const helmet = require('helmet'); // Models const User = require("./models/user.js"); // Passport Config const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; const flash = require("connect-flash"); const session = require("express-session"); const cookieParser = require("cookie-parser"); // 省略中间件和路由导入... const db_url = process.env.DB_URL; const PORT = process.env.PORT || 5000; const secret = process.env.SESSION_SECRET; const corsOptions = { origin: [ "http://localhost:3000", "http://localhost:5000", "https://geopilot.herokuapp.com",], credentials: true, optionSuccessStatus:200, }; app.use(cors(corsOptions)); // 省略数据库连接、session配置、passport配置... // Routes app.use("/api/country/:countryId/reviews", reviewRoute); app.use("/api/country", countryRoute); // 省略其他路由... // ----------- Deployment ----------- __dirname = path.resolve(); if (process.env.NODE_ENV === "production") { app.use(express.static(path.join(__dirname, "../client/build"))); app.get("*", (req, res) => { res.sendFile(path.resolve(__dirname, "../client", "build", "index.html")); }); } // ----------- Deployment ----------- app.get("*", () => { res.send("PAGE NOT FOUND"); });
已尝试但无效的方案
- Postman测试接口正常返回
- 修改package.json代理配置及服务器启动脚本
- 切换端点并调整CORS策略
排查与解决步骤
1. 统一前端接口路径前缀
注意到Services中部分接口未带/api前缀(如/country/getAll),但后端所有API路由均挂载在/api下,这类请求在生产环境会被静态文件路由拦截,返回index.html导致失败。需统一所有接口路径:
// 修改Services中的接口路径 getAllCountries() { return http.get("/api/country/getAll"); } getScroll(skip) { return http.get(`/api/country?skip=${skip}`); } get(id) { return http.get(`/api/country/${id}`); } create(countryForm) { return http.post("/api/country/new", countryForm); } edit(id, values) { return http.put(`/api/country/${id}`, values); } delete(id) { return http.delete(`/api/country/${id}`); }
2. 修正Session Cookie生产环境配置
Heroku使用HTTPS,需调整Cookie的secure和sameSite参数,同时开启反向代理信任:
// 在session配置前添加 app.set('trust proxy', 1); // 修改sessionOptions中的cookie配置 const sessionOptions = { name: "geopilot_session", secret: secret, store: store, resave: false, saveUninitialized: false, cookie: { samesite: process.env.NODE_ENV === "production" ? 'none' : false, httpOnly: true, secure: process.env.NODE_ENV === "production", expires: Date.now() + 1000 * 60 * 60 * 24 * 365, maxAge: 1000 * 60 * 60 * 24 * 365, }, };
3. 确认前端环境变量设置
在Heroku控制台的应用Settings -> Config Vars中添加REACT_APP_NODE_ENV=production,确保前端axios的生产环境baseURL逻辑生效。
4. 优化后端路由顺序
删除后端最后冗余的app.get("*")路由,生产环境下该路由会被前面的静态文件*路由拦截,无实际作用,避免逻辑混淆。
5. 验证CORS配置准确性
确认Heroku应用的实际域名与corsOptions中的https://geopilot.herokuapp.com完全一致,若使用自定义域名需同步更新origin列表:
const corsOptions = { origin: process.env.NODE_ENV === "production" ? ["https://geopilot.herokuapp.com"] : ["http://localhost:3000", "http://localhost:5000"], credentials: true, optionSuccessStatus:200, };
总结
最核心的问题是前端部分接口缺少/api前缀导致请求被静态文件路由拦截,其次是Session Cookie的生产环境配置不符合HTTPS要求。按照上述步骤修复后,重新部署Heroku应用即可解决连接拒绝问题。
内容的提问来源于stack exchange,提问作者Isaiahm
相关产品推荐
相关产品推荐

