React应用请求指向EC2服务器而非本地的配置问题
你的核心误解在于:React单页应用是在用户浏览器中运行的,所有API请求都是从用户的本地浏览器发起,而非EC2服务器内部。所以你写的http://localhost:3000/api会被用户的浏览器解析成他们自己电脑的3000端口,而非EC2实例的3000端口——这就是关闭本地服务后请求失败的原因。
你确实可以不暴露Express API的公网端点,让React正常调用,下面是两种实用方案:
方案一:Apache反向代理(最推荐)
利用已有的Apache服务器做反向代理,把React发起的API请求转发到EC2本地的3000端口,全程不需要给Express开公网端口。
先启用Apache必要的模块:
sudo a2enmod proxy proxy_http proxy_html sudo systemctl restart apache2修改React应用的API请求地址,把
http://localhost:3000/api改成和前端同域名的路径,比如/api。编辑你的Apache虚拟主机配置文件(比如
/etc/apache2/sites-available/your-react-site.conf),添加反向代理规则:<VirtualHost *:80> ServerName your-domain.com # 替换成你的域名或EC2公网IP # 处理React静态文件 DocumentRoot /var/www/your-react-app-build <Directory /var/www/your-react-app-build> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> # 把/api请求转发到EC2内部的3000端口 ProxyPass /api http://localhost:3000/api ProxyPassReverse /api http://localhost:3000/api </VirtualHost>重启Apache生效:
sudo systemctl restart apache2
这样用户浏览器发起的/api请求会被Apache转发到EC2内部的Express服务,API完全不需要暴露公网端口。
方案二:Express与React同端口部署
把React的静态构建文件交给Express托管,让Express同时服务前端页面和API接口,也不需要额外暴露端口:
在React项目中完成生产构建:
npm run build把生成的
build文件夹复制到Express项目的根目录下。修改Express的入口文件(比如
app.js),添加静态资源服务和SPA路由 fallback:const express = require('express'); const path = require('path'); const app = express(); // 托管React静态文件 app.use(express.static(path.join(__dirname, 'build'))); // 你的API路由 app.use('/api', require('./routes/api')); // 处理React单页路由的刷新问题 app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); app.listen(3000, () => { console.log('Server running on port 3000'); });之后可以用Apache反向代理80端口到Express的3000端口(参考方案一的代理规则,只需要把根路径代理到
http://localhost:3000),或者直接让Express监听80端口(需要sudo权限)。
这种方案下所有请求走同一个端口,API完全在EC2内部处理,不会暴露公网端点。
补充:环境变量适配多环境
为了避免本地开发和生产环境的API地址来回修改,推荐在React项目中用环境变量配置:
本地开发时,在
.env.development中设置:REACT_APP_API_URL=http://localhost:3000/api生产部署时,在
.env.production中设置:REACT_APP_API_URL=/api
然后在React代码中通过process.env.REACT_APP_API_URL调用API,本地和生产环境会自动切换地址。
内容的提问来源于stack exchange,提问作者Jorge Zapata

