Node.js服务跑在3000端口,如何让用户通过80端口正常访问?很多项目在开发阶段直接使用node app.js启动,但生产环境不可能让用户记住端口号,也不可能给每个Node进程分配独立公网IP。这时候Apache可以作为反向代理,接收标准HTTP或HTTPS请求,再转发给本机的Node.js进程。这样做还能顺便处理静态文件、SSL终止、访问日志和负载均衡,让Node.js专注业务逻辑。

一、加载必要的Apache模块
反向代理功能由mod_proxy模块提供,而针对HTTP协议的具体转发则需要mod_proxy_http子模块。如果Node.js应用只处理普通HTTP请求,启用这两个模块就够了;如果涉及WebSocket长连接,还需要mod_proxy_wstunnel。不同Linux发行版启用模块的方式略有差异,但最终都体现在Apache主配置的LoadModule指令上。
在Debian或Ubuntu系统中,可以直接使用a2enmod命令完成模块启用,系统会自动在mods-enabled目录创建符号链接。执行完毕后必须重启Apache使模块生效。CentOS、RHEL等系统通常需要手动编辑httpd.conf,或者检查模块文件是否已经放在modules目录下并通过LoadModule加载。建议先运行apachectl -M | grep proxy查看当前已加载的代理相关模块,避免重复加载或遗漏。
# Debian/Ubuntu 启用模块 sudo a2enmod proxy sudo a2enmod proxy_http sudo systemctl restart apache2 # CentOS/RHEL 检查模块是否已加载 httpd -M | grep proxy # 如果没有,需要在 httpd.conf 中添加如下行 LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so
模块加载完成后,Apache就具备了把请求转发到后端服务器的能力。但真正决定哪些请求需要转发、转发到哪里,还要依赖VirtualHost中的具体指令。接下来进入核心配置部分。
二、配置VirtualHost实现反向代理
反向代理的核心指令是ProxyPass和ProxyPassReverse。ProxyPass负责定义路径映射关系,将某个URL前缀的请求转发到后端服务器;ProxyPassReverse则用于重写后端响应头中的Location、Content-Location等字段,防止后端跳转时把内部端口暴露给客户端。如果只写ProxyPass而不写ProxyPassReverse,Node.js返回302重定向时可能会把localhost:3000直接发给浏览器,导致用户访问失败。
下面是一个完整的虚拟主机配置示例。假设Node.js服务监听在127.0.0.1:3000,域名使用ipipp.com,所有请求都转发给Node处理。注意ProxyPass路径末尾的斜杠非常关键:如果写成ProxyPass / http://127.0.0.1:3000,请求 /api 会被转发为 http://127.0.0.1:3000api(缺少斜杠);而写成ProxyPass / http://127.0.0.1:3000/ 则会正确转发为 http://127.0.0.1:3000/api。
<VirtualHost *:80>
ServerName ipipp.com
ServerAlias www.ipipp.com
# 关闭正向代理,防止被滥用
ProxyRequests Off
# 允许通过本地代理
<Proxy *>
Require all granted
</Proxy>
# 核心转发规则
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# 可选:设置超时时间
ProxyTimeout 60
ErrorLog ${APACHE_LOG_DIR}/node-proxy-error.log
CustomLog ${APACHE_LOG_DIR}/node-proxy-access.log combined
</VirtualHost>
这段配置先关闭了正向代理功能,这是安全上的基本要求。<Proxy *>块控制哪些客户端可以经由这个代理访问后端,这里使用Require all granted允许所有请求,但实际环境中可以根据需要收紧为仅限本地网络或特定IP。ProxyTimeout设置Apache等待后端响应的最长时间,如果Node.js处理时间较长可以适当调大。
如果只想代理特定路径,比如 /api 开头的请求转发给Node.js,其余请求继续由Apache处理静态文件,可以这样写:ProxyPass /api http://127.0.0.1:3000/api 和 ProxyPassReverse /api http://127.0.0.1:3000/api。注意此时路径末尾不加斜杠,否则匹配规则会变化。这种混合模式在既有静态站点又要接入API服务时非常实用。
三、处理WebSocket与长连接
如果Node.js应用使用Socket.IO或原生WebSocket,普通的HTTP代理配置无法处理协议升级请求。WebSocket连接首先通过HTTP发起,携带Upgrade: websocket和Connection: Upgrade请求头,要求服务器切换到TCP长连接。Apache需要mod_proxy_wstunnel模块来识别并转发这类升级请求,否则浏览器会收到400或502错误。
启用mod_proxy_wstunnel后,可以在VirtualHost中增加针对WebSocket路径的转发规则。通常需要配合RewriteEngine将特定路径的请求重写为ws://或wss://协议,然后由ProxyPass处理。以下配置演示了如何让 /socket.io/ 路径的请求升级为WebSocket连接,同时保持其他路径的普通HTTP代理不变。
<VirtualHost *:80>
ServerName ipipp.com
ProxyRequests Off
ProxyPreserveHost On
# 普通HTTP代理
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# WebSocket 升级处理
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule /socket.io/(.*) ws://127.0.0.1:3000/socket.io/$1 [P,L]
# 或者使用 ProxyPass 配合 upgrade 参数
# ProxyPass /socket.io/ ws://127.0.0.1:3000/socket.io/
# ProxyPassReverse /socket.io/ ws://127.0.0.1:3000/socket.io/
</VirtualHost>
ProxyPreserveHost On 的作用是让Apache把原始Host头传递给Node.js后端,这样Node应用拿到的是用户访问的域名而不是127.0.0.1,对于生成绝对URL或校验Host的场景很重要。如果后端需要根据Host做虚拟主机区分,这个指令必须开启。
RewriteRule中的[P]标志表示通过mod_proxy进行代理转发,[L]表示匹配后停止后续规则。使用ws://协议可以触发mod_proxy_wstunnel处理升级。如果Node.js服务启用了HTTPS,则对应使用wss://。另外,某些旧版Apache不支持这种写法,可以显式使用ProxyPass /socket.io/ ws://127.0.0.1:3000/socket.io/ 加上upgrade=websocket参数,具体取决于Apache版本。
四、保留客户端真实IP与安全加固
配置反向代理后,Node.js通过request.socket.remoteAddress拿到的地址永远是127.0.0.1,因为连接是Apache发起的。如果需要记录真实客户端IP用于日志分析、限流或风控,必须让Apache在转发请求时添加X-Forwarded-For头,并且Node.js端要信任这个头。Apache默认可能不会自动添加,需要显式开启远程IP处理模块。
较新的Apache版本可以使用mod_remoteip模块来恢复客户端真实IP,它会根据配置的代理IP列表,把X-Forwarded-For中的地址还原到RemoteIP变量。配置示例如下:RemoteIPHeader X-Forwarded-For,RemoteIPTrustedProxy 127.0.0.1。这样Apache的访问日志就能显示真实IP,同时Node.js如果使用了相应的中间件也能正确读取。
# 在 httpd.conf 或 VirtualHost 中启用 mod_remoteip
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1
# 如果需要修改日志格式,可以添加 %a 打印真实IP
LogFormat "%a %l %u %t \"%r\" %>s %b" proxy
CustomLog ${APACHE_LOG_DIR}/access.log proxy
在Node.js端,如果使用的是Express框架,可以设置app.set('trust proxy', true)来告诉Express信任X-Forwarded-For头,之后req.ip就会返回真实客户端地址。如果不想使用mod_remoteip,也可以在Apache中手动添加RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}e",但这种方式容易造成头被伪造,必须配合RemoteIPTrustedProxy限制来源。
安全方面,最重要的一点是确保Node.js端口只监听在127.0.0.1,而不是0.0.0.0。如果Node.js监听0.0.0.0:3000,外部可以直接绕过Apache访问后端,代理就形同虚设。启动时使用node app.js --host 127.0.0.1 或在代码中显式指定listen('127.0.0.1', 3000)。同时,Apache的<Proxy *>块应尽量收紧权限,避免被当作开放代理滥用。如果只有本机能够访问后端,可以写成Require ip 127.0.0.1。
完成以上配置后,重启Apache并通过浏览器访问域名即可验证。可以在Node.js中写一个简单的测试服务,输出请求头信息,确认代理是否正确传递了Host、X-Forwarded-For等字段。整个部署过程不需要修改Node.js代码,对已有项目几乎无侵入,是接入现有Apache环境的低成本方案。
Apache反向代理Node.jsmod_proxy修改时间:2026-09-22 20:03:37