Node.js 与 Angular 的 HTTPS 端口冲突及 Apache /api 反向代理配置
1. 前端和 Node 服务可以共用同一个域名
可以。浏览器只访问 Apache 的 443 端口,由 Apache 根据路径区分:
https://www.yourdomain.com/:Angular 前端https://www.yourdomain.com/api/...:反向代理到 Node.js 服务
Node.js 的 8080 或 8443 端口不需要暴露给公网,浏览器也不会直接连接这些端口。
2. 推荐做法:Apache 终止 HTTPS,Node 仅监听本地 HTTP
生产环境中,如果 Apache 和 Node.js 在同一台服务器上,常见做法是:
- 浏览器到 Apache 使用 HTTPS。
- Apache 到本机 Node.js 使用普通 HTTP。
- Node.js 只监听
127.0.0.1,不接受公网连接。
因为流量只经过本机回环地址,不需要再封装一层 TLS。这样也只需要在 Apache 上配置一张公开证书。
修改 Node.js:
const http = require('http'); // app 是 Express 实例 app.set('trust proxy', 'loopback'); http.createServer(app).listen(8080, '127.0.0.1', () => { console.log('Node.js listening on http://127.0.0.1:8080'); });
这里可以删除 Node 中读取 server.key、server.crt、server.pem 并创建 HTTPS 服务的代码。
3. 修改 Apache 的 443 VirtualHost
确保启用代理模块:
sudo a2enmod proxy proxy_http headers sudo apache2ctl configtest sudo systemctl restart apache2
推荐配置如下:
<VirtualHost *:80> ServerName yourdomain.com ServerAlias www.yourdomain.com Redirect 301 / https://www.yourdomain.com/ </VirtualHost> <VirtualHost *:443> ServerName www.yourdomain.com ServerAlias yourdomain.com DocumentRoot /var/www/your-angular-app SSLEngine on SSLCertificateFile /etc/ssl/private/server.crt SSLCertificateKeyFile /etc/ssl/private/server.key SSLCACertificateFile /etc/ssl/private/gd_bundle.crt ProxyRequests Off ProxyPreserveHost On RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-Port "443" ProxyPass /api http://127.0.0.1:8080/api ProxyPassReverse /api http://127.0.0.1:8080/api <Directory /var/www/your-angular-app> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> </VirtualHost>
注意:
- 把
yourdomain.com和/var/www/your-angular-app替换成实际值。 gd_bundle.crt应是 GoDaddy 提供的中间证书链,不要把站点证书本身重复放在这里。- 如果证书不包含
www.yourdomain.com,浏览器访问www时仍会报证书错误。一张证书可以通过 SAN 同时包含yourdomain.com和www.yourdomain.com,必要时让 GoDaddy 重新签发。 - Angular 前端应使用相对路径
/api请求接口,不要写https://IP:8443。
上面的配置假设 Express 路由本身带有 /api 前缀,例如:
app.get('/api/users', ...);
如果 Express 路由不带 /api,而是 /users,则应使用下面的写法把前缀去掉:
ProxyPass /api/ http://127.0.0.1:8080/ ProxyPassReverse /api/ http://127.0.0.1:8080/ ProxyPass /api http://127.0.0.1:8080 ProxyPassReverse /api http://127.0.0.1:8080
4. 防止 Angular 把 /api 重写成 index.html
如果 Angular 的 .htaccess 中有把所有路径重写到 index.html 的规则,需要排除 /api:
RewriteCond %{REQUEST_URI} !^/api RewriteRule ^ index.html [L]
否则代理可能没有被正确命中,请求最终仍返回 Angular 的 index.html。
5. 为什么原来的 HTTPS 代理配置不生效
你之前的配置:
ProxyPass /api https://myip:8443/api
有两个问题:
Apache 作为代理客户端连接 HTTPS 后端时,需要配置:
SSLProxyEngine On证书签发给的是域名,而不是 IP。连接
https://myip:8443时,证书中的域名和访问地址不匹配,Apache 会拒绝后端连接。
即使加上 SSLProxyEngine On,直接使用 IP 仍会产生证书主机名校验错误。生产环境不建议通过关闭校验绕过。
6. 如果 Apache 和 Node 不在同一台机器
如果后端网络不可信,仍然建议 Apache 到 Node.js 使用 HTTPS。此时应:
- 使用内部 CA 给 Node 签发包含内部主机名的证书;
- Apache 配置
SSLProxyEngine On; - 使用能和证书 SAN 匹配的主机名,而不是公网 IP;
- 不要关闭证书校验。
示例:
SSLProxyEngine On SSLProxyCACertificateFile /etc/ssl/private/internal-ca.pem ProxyPass /api https://node.internal:8443/api ProxyPassReverse /api https://node.internal:8443/api
如果证书使用 IP 访问,则证书必须包含对应的 IP SAN。不要在生产环境使用 SSLProxyVerify none 或关闭 PeerName 校验。
修改 443 端口的 VirtualHost,在其中加入反向代理;Node.js 改为只监听 127.0.0.1:8080 的 HTTP 服务。浏览器继续访问 https://www.yourdomain.com/api,Apache 会把该路径转发到本机 Node.js,Angular 则处理其他路径。一张公开 SSL 证书只需要安装在 Apache 上,Node.js 不需要直接暴露 8443 端口,也不需要使用 IP 访问。
内容的提问来源于 stack exchange,提问作者 Nosk48

