Nginx 反向代理完全入门:从一个真实场景开始
在服务部署中,经常会遇到这样的场景:
一个在本地
8080端口启动的服务(例如 Spring Boot 应用或 Node.js API),需要通过https://mydomain.com/api这样的公共地址对外提供访问。
这便是 反向代理 的典型应用场景。Nginx 作为网关,负责接收所有用户的请求,然后根据预设规则,将请求转发给内部的实际服务。
本文将介绍如何一步步搭建一个健壮的反向代理,并详细解释其中最关键的两个知识点:proxy_pass 的斜杠规则 和 请求头的正确传递。
第一步:实现基础代理
首先解决上面提出的问题。目标是:当用户访问 .../api/... 时,Nginx 将请求转发给 http://127.0.0.1:8080。
在 Nginx 配置文件 server 块中,加入以下 location 配置:
nginx
location /api/ {
# 将请求转发给本地的 8080 端口
proxy_pass http://127.0.0.1:8080/;
}
重启 Nginx 后,访问 https://mydomain.com/api/users,Nginx 会返回 http://127.0.0.1:8080/users 的内容。至此,最核心的代理功能已经完成。
第二步:理解 proxy_pass 的斜杠规则
proxy_pass 指令后的斜杠 / 是一个关键细节,它决定了 Nginx 如何处理和转发 URL。
以访问 https://mydomain.com/api/users 为例进行说明。
情况一:proxy_pass 带斜杠(常用)
这是刚才使用的配置:
proxy_pass http://127.0.0.1:8080/; (结尾有 /)
- 行为 :当
proxy_pass的地址以/结尾时,Nginx 会将location匹配到的路径从原始请求 URI 中移除,然后将剩余部分拼接到proxy_pass的地址后面。 - 转换过程 :
- 原始请求路径:
/api/users - Nginx 移除匹配的
/api/,剩下:users - 最终后端收到的路径:
/users
- 原始请求路径:
- 适用场景 :这是最常见的用法。后端服务(如 Spring Boot)的路由通常从根路径
/开始(例如/users,/products),并不知道外部代理为其添加了/api前缀。
情况二:proxy_pass 不带斜杠
如果去掉结尾的斜杠:
proxy_pass http://127.0.0.1:8080; (结尾没有 /)
- 行为 :Nginx 会将完整的原始请求 URI 直接拼接到
proxy_pass的地址后面。 - 转换过程 :
- 原始请求路径:
/api/users - 最终后端收到的路径:
/api/users
- 原始请求路径:
- 适用场景 :当后端服务本身已经配置了
/api这个前缀(Context Path)时使用。
小结与速查
| 配置方式 | proxy_pass 结尾 | 原始 URL | 后端收到的路径 | 行为描述 |
|---|---|---|---|---|
| 方式 A | http://ip:port/ (有斜杠) |
/api/users |
/users |
切除前缀 (常用) |
| 方式 B | http://ip:port (无斜杠) |
/api/users |
/api/users |
保留前缀 (透传) |
| 方式 C | http://ip:port/v1/ |
/api/users |
/v1/users |
替换前缀 |
第三步:传递真实的请求头信息 proxy_set_header
经过反向代理后,后端服务直接感知到的请求方是 Nginx 服务器,而不是原始客户端。这会导致后端服务丢失关于真实用户和原始请求的关键信息。
proxy_set_header 指令可以将原始请求的重要信息添加到转发给后端的请求头中。
以下是推荐在多数场景下使用的标准配置:
nginx
location /api/ {
proxy_pass http://127.0.0.1:8080/;
# === 核心 Header 传递 ===
# 1. 传递客户端请求的域名
proxy_set_header Host $host;
# 2. 传递客户端的真实 IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 3. 传递客户端使用的协议 (http/https)
proxy_set_header X-Forwarded-Proto $scheme;
}
Header 的作用
-
Host $host;: 传递客户端请求的原始域名。如果后端服务需要根据域名进行逻辑判断(如多站点部署),此值至关重要。若不设置,后端获取到的 Host 可能是127.0.0.1:8080。 -
X-Real-IP和X-Forwarded-For: 传递客户端的真实 IP 地址。$remote_addr是直接连接到 Nginx 的客户端 IP。$proxy_add_x_forwarded_for会记录请求经过的每一层代理的 IP 链条,格式为用户IP, CDN节点IP, ...。- 若不设置,后端日志记录的访问 IP 将永远是 Nginx 服务器的 IP,这对日志分析和安全审计非常不利。
-
X-Forwarded-Proto $scheme: 传递客户端使用的协议(http或https)。- 场景 :Nginx 对外提供
https服务,但 Nginx 与后端之间通过http通信以提高性能。 - 如果后端不知道客户端正在使用
https,在执行重定向(如登录后跳转)时,可能会生成一个http://链接。这会导致浏览器报告"混合内容"错误,或将用户从安全的https页面降级到不安全的http页面。
- 场景 :Nginx 对外提供
第四步:解决常见的静态资源 404 问题
一个常见的问题是:代理配置完成后,主页的 HTML 内容成功加载,但页面样式、图片等静态资源无法加载,浏览器控制台报告大量 404 错误。
问题根源
该问题通常源于应用代码中资源路径的写法。
假设后端返回的 HTML 中包含以下 CSS 引用:
<link rel="stylesheet" href="/css/style.css">
这是一个根路径。浏览器会据此发起一个独立的请求,地址为:
https://mydomain.com/css/style.css
由于 Nginx 配置中只有 /api/ 路径的代理规则,没有匹配 /css/ 的规则,因此这个请求无法被正确处理,导致 404 错误。
解决方案(推荐)
最佳实践是配置应用,使其感知到自身运行在特定的子路径下。
- 前端应用 (Vue/React) :在打包配置中设置
publicPath或base。例如,在vue.config.js中设置为publicPath: '/api/'。 - 后端应用 (Spring Boot) :在
application.properties中配置server.servlet.context-path=/api。 - 后端应用 (Swagger UI) :需要配置相关的路径参数,使其知道基础路径是
/api。
修改后,应用生成的 HTML 资源路径会自动带上 /api 前缀,变为:
<link rel="stylesheet" href="/api/css/style.css">
这样,浏览器请求的 https://mydomain.com/api/css/style.css 就能被 location /api/ 规则正确捕获并代理到后端。
完整的代理配置示例
综合以上所有知识点,一个健壮、通用的反向代理配置如下:
nginx
# ----------------------------------------------------
# API 子路径代理
# 访问入口: https://mydomain.com/api/
# ----------------------------------------------------
location /api/ {
# 1. 代理规则 (切掉 /api/ 前缀)
proxy_pass http://127.0.0.1:8080/;
# 2. 传递核心 Header
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 3. WebSocket 支持 (可选,如果后端需要)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 4. 超时设置 (可选,如果接口耗时较长建议调大)
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# 5. 自动修正后端重定向地址 (可选,但推荐)
# 如果后端返回一个重定向到 /login,Nginx 会自动修正为 /api/login
proxy_redirect default;
}
希望这篇教程能帮助读者更好地理解和应用 Nginx 反向代理。