Heroku部署Node+React应用刷新/posts路由报Cannot GET错误如何解决
问题根因
该问题属于React单页应用(SPA)生产部署的典型配置遗漏,和业务代码逻辑无关。
本地开发时,webpack-dev-server默认开启了history路由回退机制:无论访问什么子路径,服务都会优先返回根目录的index.html,再由React Router在前端完成路由匹配,因此刷新子路由页面不会报错。
部署到Heroku生产环境后,默认的服务配置没有开启该回退规则,服务端收到/posts这类GET请求时,会直接查找服务端对应路径的实体资源,找不到资源就会返回Cannot GET /posts的404错误。
修复步骤
- 第一步:确认React路由模式
检查项目中React Router的引入配置,如果你使用的是BrowserRouter(路径不带#的history模式路由),才会触发该问题;如果使用HashRouter(路径带#标识)不会出现该类报错,但URL美观度较差。 - 第二步:根据你的部署方式配置服务端路由回退
- 如果你用自定义Express服务托管React构建产物:
找到Express服务入口文件(通常为server.js/app.js),在所有接口路由、静态资源托管配置的最后,添加通配路由回退逻辑,将所有非API的GET请求统一返回index.html,示例配置如下:
注意:通配路由const path = require('path'); const express = require('express'); const app = express(); // 静态资源托管配置 app.use(express.static(path.join(__dirname, 'build'))); // 此处放置你的业务API路由,例如 app.get('/api/list', ...) // 核心:history模式路由回退,必须放在所有路由配置的最末尾 app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); const PORT = process.env.PORT || 3000; app.listen(PORT);*的位置不能前置,否则会拦截正常的接口、静态资源请求。 - 如果你是直接使用create-react-app默认构建配置,没有自定义Node服务:
在项目根目录新建static.json配置文件,Heroku静态资源构建包会自动识别该配置完成路由回退,文件内容如下:{ "root": "build/", "clean_urls": false, "routes": { "/**": "index.html" } }
- 如果你用自定义Express服务托管React构建产物:
- 第三步:校验Heroku部署配置
- 检查
package.json中的build脚本配置正确,CRA项目默认脚本为"build": "react-scripts build",Heroku部署时会自动执行该脚本生成构建产物目录 - 检查
.gitignore文件,确保build目录没有被忽略提交 - 代码推送到Heroku仓库后,执行
heroku restart重启服务实例,避免旧配置缓存影响结果
- 检查
验证方式
重新部署完成后,直接访问线上子路由地址如https://xyz.herokuapp.com/posts,刷新页面如果可以正常加载对应页面内容,不返回Cannot GET错误,即表示配置生效。
内容的提问来源于stack exchange,提问作者Abby
相关产品推荐
相关产品推荐

