反向代理是 Nginx 最核心的功能之一。简单来说,客户端向 Nginx 发送请求,Nginx 再将请求转发给后端的真实服务器,最后将后端服务器的响应返回给客户端。对客户端而言,它只与 Nginx 交互,并不知道后端服务器的存在。
主要作用:
- 隐藏后端服务:保护后端服务器的真实 IP 和架构。
- 统一入口:为多个后端服务提供一个统一的访问地址。
- 负载均衡:将请求分发到多个后端服务器,提高系统处理能力。
- HTTPS 卸载:由 Nginx 统一处理 HTTPS 加密解密,减轻后端服务器负担。
核心指令 proxy_pass
proxy_pass 是实现反向代理的关键指令,用于指定后端服务器的地址。
关键细节:末尾斜杠 / 的行为差异
proxy_pass 指令末尾是否带斜杠 /,会直接影响转发到后端的路径,这是一个常见的配置陷阱。
假设 Nginx 配置如下:
# 写法 A: 不带斜杠
location /api/ {
proxy_pass http://backend_server;
}
# 写法 B: 带斜杠
location /api/ {
proxy_pass http://backend_server/;
}
当客户端请求 http://nginx_ip/api/users 时:
- 写法 A (无斜杠) :Nginx 会将完整的 URI
/api/users转发给后端,即后端收到的是http://backend_server/api/users。 - 写法 B (有斜杠) :Nginx 会剥离
location匹配的部分/api/,只将剩余部分users转发给后端,即后端收到的是http://backend_server/users。
配置建议:
根据后端服务的路由设计来决定使用哪种写法。如果后端服务期望接收 /api/ 前缀,则用写法 A;如果后端服务的路由不包含此前缀,则用写法 B。
推荐配置:传递真实客户端信息
为了让后端服务能获取到客户端的真实 IP 等信息,通常需要配合以下指令使用:
location / {
proxy_pass http://backend_server;
# 传递原始 Host 头
proxy_set_header Host $host;
# 传递客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
# 传递完整的代理链 IP 信息
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
🛠️ 综合配置示例
下面是一个结合了 location 正则匹配和反向代理的实战示例,常用于前后端分离的项目部署。
场景:
-
前端静态文件(HTML, CSS, JS)由 Nginx 直接提供服务。
-
所有
/api/开头的接口请求,都反向代理到后端的 Node.js 服务。server {
listen 80;
server_name example.com;# 1. 前端静态资源服务 # 使用普通前缀匹配,指向静态文件目录 location / { root /var/www/frontend/dist; index index.html; # 支持前端路由 History 模式 try_files $uri $uri/ /index.html; } # 2. API 接口反向代理 # 使用普通前缀匹配,将所有 /api/ 请求转发给后端 location /api/ { # 假设后端服务运行在 127.0.0.1:3000 # 使用带斜杠的写法,将 /api/ 前缀剥离 proxy_pass http://127.0.0.1:3000/; # 传递客户端真实信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 3. 静态资源缓存优化 # 使用正则匹配,对带哈希值的静态文件设置长期缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; }}
通过以上配置,Nginx 可以根据请求路径的不同,智能地将请求分发给静态文件目录或后端应用服务器,实现了动静分离和高效的反向代理。
try_files 的详细解析:
1. 核心语法与工作原理
try_files file1 [file2 ...] [uri | @named_location | =code];
工作流程:
Nginx 会按照你列出的顺序,依次检查文件或目录是否存在(相对于 root 或 alias 定义的根目录):
- 检查文件 :检查
file1是否存在。 - 检查下一个 :如果不存在,检查
file2,以此类推。 - 执行回退 :如果前面列出的所有文件都不存在,则执行最后一个参数。
关键点:
- 最后一个参数是回退操作,它必须存在。
- 前面的参数只是用来"试错"的。
2. 最常见的应用场景
场景 A:解决单页应用(SPA)刷新 404 问题
这是目前前端开发中最常用的场景。Vue、React 等框架使用 History 模式路由时,URL 可能是 /user/profile,但服务器上并没有这个真实目录。
location / {
root /var/www/my-app;
# 1. 尝试找文件($uri) -> 2. 尝试找目录($uri/) -> 3. 都不存在则返回 /index.html
try_files $uri $uri/ /index.html;
}
- 逻辑 :如果用户访问
/about,Nginx 发现没有about文件,也没有about/目录,于是内部重定向到/index.html,让前端 JS 接管路由渲染页面。
场景 B:动静分离与反向代理(配合"动态代理")
如果你想实现"有静态文件就返回静态文件,没有则交给后端动态处理",可以结合 try_files 和 @named_location(命名 location)。
配置:
location / {
# 1. 尝试找文件 -> 2. 尝试找目录 -> 3. 都不存在则交给 @backend 处理
try_files $uri $uri/ @backend;
}
# 定义后端代理
location @backend {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
# ...其他代理配置
}
- 逻辑:这完美体现了你提到的"动态代理"概念。Nginx 优先服务静态资源,只有当静态资源找不到时,才将请求转发给后端应用服务器(如 Tomcat, Node.js, Go 等),减轻后端压力。
场景 C:返回指定错误码
如果你不希望找不到文件时重定向,而是直接报错:
配置:
location /images/ {
# 找不到文件直接返回 404
try_files $uri =404;
}
3. 参数详解与避坑指南
| 参数类型 | 说明 | 示例 |
|---|---|---|
| $ uri | 匹配请求的原始 URI 对应的文件 | try_files $uri ... |
| $ uri/ | 匹配请求 URI 对应的目录(会尝试寻找 index 文件) | try_files $uri/ ... |
| /path/to/file | 指定的具体文件(相对于 root) | try_files /fallback.html ... |
| @name | 命名 location,用于内部跳转(常用于 proxy_pass) | try_files $uri @backend |
| =404 | 直接返回指定的 HTTP 状态码 | try_files $uri =404 |
⚠️ 常见错误(死循环陷阱):
千万不要在正则 location 中错误地使用 try_files,否则会导致 500 错误。
错误示范:
# 错误!会导致死循环
location ~ \.jpg$ {
try_files /static$uri $uri;
}
- 原因 :如果
/static/test.jpg不存在,try_files会回退到最后一个参数$uri。这会触发内部重定向,再次匹配到location ~ \.jpg$,再次尝试,无限循环,直到 Nginx 报错500 Internal Server Error。 - 修正:最后一个参数不能是会导致再次匹配到同一个 location 的 URI。
4. 总结
try_files 的核心逻辑就是**"层层递进,最后兜底"**。
- 前端部署 :
try_files $uri $uri/ /index.html;(标准写法,必背)。 - 动静结合 :
try_files $uri @backend;(先找静态,找不到转动态代理)。 - 路径基准 :所有的文件路径检查都是基于当前
location块中的root或alias指令的。