一、引言:为什么你的架构离不开反向代理?
想象一下,你正在开发一个应用,前端是 React/Vue,后端是 Java/Go/Node.js。在开发阶段,它们运行在不同的端口上(如 3000 和 8080),这导致了恼人的跨域问题。
再想象一下,你的应用用户量激增,单台后端服务器已经不堪重负,你需要引入多台服务器来分担负载。
还有,你希望隐藏后端服务器的真实 IP 和技术栈,以增加系统的安全性。
所有这些问题的答案,都指向同一个核心技术------Nginx 反向代理。
它就像一个不知疲倦的"全能门卫",站在你的应用最前方,负责接收所有请求,智能地将其分发给后端服务,并将结果安全地返回给用户。本文将带你从原理到实战,彻底掌握这项必备技能。
💡 核心价值 :
学会 Nginx 反向代理,你就掌握了构建高可用、高性能、安全的现代 Web 架构的第一块基石!
二、正向代理 vs. 反向代理:一张图看懂区别
很多初学者容易混淆这两个概念,其实它们的服务对象完全不同。
-
正向代理 (Forward Proxy):
- 服务对象 :客户端。
- 作用:客户端通过代理去访问外部网络(如翻墙、公司内网访问外网)。服务器只知道代理的 IP,不知道真实客户端是谁。
- 类比:代购。你(客户端)委托代购(代理)去国外买东西(服务器)。
-
反向代理 (Reverse Proxy):
- 服务对象 :服务器。
- 作用:客户端直接访问代理,代理再将请求转发给内部的一台或多台真实服务器。客户端只知道代理的 IP,不知道后端服务器的存在。
- 类比:公司前台。访客(客户端)只和前台(代理)打交道,前台再根据需求将访客引导到具体的部门(后端服务器)。
Nginx 的核心角色就是反向代理。
三、核心指令:proxy_pass
proxy_pass 是 Nginx 反向代理功能的心脏。它的作用就是告诉 Nginx:"把匹配到这个 location 的请求,转发到我指定的地址去"。
基本语法
location /some/path/ {
proxy_pass http://backend_server;
}
关键细节:URI 的处理
proxy_pass 后面的 URL 是否以 / 结尾,会直接影响转发时的 URI。
场景 :客户端请求 http://proxy.com/api/users
-
情况一:
proxy_pass以/结尾location /api/ { proxy_pass http://192.168.1.10:8080/; # 注意结尾的 / }转发结果 :
http://192.168.1.10:8080/users解释 :
/api/被替换成/,所以/api/users变成了/users。 -
情况二:
proxy_pass不以/结尾location /api/ { proxy_pass http://192.168.1.10:8080; # 没有结尾的 / }转发结果 :
http://192.168.1.10:8080/api/users解释 :整个原始 URI (
/api/users) 会被完整地附加到proxy_pass的 URL 后面。
最佳实践 :为了精确控制后端接收到的路径,强烈建议使用第一种方式(带 /) ,并在 location 中明确匹配前缀。
四、生产级配置:不可或缺的请求头
仅仅使用 proxy_pass 是远远不够的。后端服务器需要知道真实的客户端信息,否则日志、安全策略、业务逻辑都会出错。
必须设置的三大请求头
location / {
proxy_pass http://backend;
# 1. 传递原始Host头
# 后端可能根据域名做不同处理(如多租户)
proxy_set_header Host $host;
# 2. 传递客户端真实IP
proxy_set_header X-Real-IP $remote_addr;
# 3. 记录完整的代理链路(最重要!)
# 如果有多层代理,这个头会追加IP,形成链路
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
$host: 客户端请求中的Host头。$remote_addr: 直接与 Nginx 建立连接的客户端 IP。$proxy_add_x_forwarded_for: 这是一个智能变量。如果请求中已有X-Forwarded-For头,它会在此基础上追加$remote_addr;如果没有,则直接创建该头并赋值为$remote_addr。
重要提示 :后端代码必须从
X-Forwarded-For头中获取真实 IP,而不是直接读取 TCP 连接的对端 IP(那会是 Nginx 的 IP)。
五、六大经典应用场景
场景一:解决前后端分离的跨域问题
痛点 :前端 localhost:3000 无法直接调用后端 localhost:8080 的 API。
解决方案:在前端开发服务器或 Nginx 中配置代理。
server {
listen 80;
server_name localhost;
# 静态资源
location / {
root /path/to/frontend/dist;
try_files $uri $uri/ /index.html;
}
# API 代理,解决跨域
location /api/ {
proxy_pass http://localhost:8080/; # 转发到后端
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
现在,前端只需请求 /api/users,Nginx 会将其无缝代理到后端,浏览器认为这是同源请求,跨域问题迎刃而解。
场景二:负载均衡
通过 upstream 块定义一组后端服务器,Nginx 会自动进行负载均衡。
upstream myapp {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup; # 备份节点
}
server {
location / {
proxy_pass http://myapp; # 转发到 upstream
proxy_set_header ...; # 请求头略
}
}
场景三:HTTPS/SSL 终结
将 HTTPS 的加解密工作交给 Nginx,后端服务只需处理简单的 HTTP 流量,极大减轻后端压力。
server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/privkey.pem;
location / {
proxy_pass http://backend; # 转发的是HTTP!
proxy_set_header X-Forwarded-Proto $scheme; # 告诉后端原始是https
proxy_set_header ...;
}
}
场景四:API 网关与路由
作为微服务架构的统一入口,根据路径将请求路由到不同的服务。
location /user/ {
proxy_pass http://user-service/;
}
location /order/ {
proxy_pass http://order-service/;
}
location /payment/ {
proxy_pass http://payment-service/;
}
场景五:安全防护与隐藏后端
对外只暴露 Nginx 的 IP,后端服务器可以部署在内网,不直接暴露在公网,增加了攻击难度。
场景六:缓存静态/动态内容
Nginx 可以缓存后端返回的内容,对于不常变化的 API 或页面,能极大提升响应速度并降低后端负载。
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=my_cache:10m;
location /api/slow-data {
proxy_cache my_cache;
proxy_cache_valid 200 5m; # 缓存成功响应5分钟
proxy_pass http://backend;
}
六、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!