系统地解释 Nginx 反向代理的原理,包括概念、工作流程、核心优势和应用场景

499 阅读7分钟

✅ 什么是反向代理?

🔹 代理:本质上是中间人,客户端和服务器之间的“中转站”。
🔹 代理分为:

  • 正向代理:代理客户端,客户端通过代理去访问服务器。例如科学上网、公司网络代理。
  • 反向代理:代理服务器,客户端并不知道真正的后端服务器是谁,它只知道代理服务器的 IP 和域名。

在反向代理中:

  • 客户端 请求 Nginx;
  • Nginx 接收请求并将其转发 到后端服务器(upstream),拿到后端响应后再转发给客户端。

客户端始终认为自己只和 Nginx 通信,而不知道真实服务器的存在。


🔎 Nginx 反向代理的工作流程

1️⃣ 浏览器(或客户端)向 Nginx 发起请求:

Client --> Nginx

2️⃣ Nginx 根据配置文件(nginx.conf)中的代理规则,决定把请求转发到哪个后端服务器:

Nginx --> Backend Server (e.g., app01, app02)

3️⃣ 后端服务器处理请求后,将结果返回给 Nginx:

Backend Server --> Nginx

4️⃣ Nginx 将后端的响应再传给客户端:

Nginx --> Client

整个过程

  • 对客户端来说,请求和响应都是和 Nginx 交互。
  • Nginx 对后端服务器起到了隐藏、防护、负载均衡的作用。

🛠 原理核心:为什么需要反向代理?

  • 安全性:隐藏真实服务器 IP,防止直接攻击后端。
  • 负载均衡:可以分发请求到多台后端,实现水平扩展。
  • 缓存能力:Nginx 可以缓存后端返回的静态资源,减轻后端负载。
  • SSL 终止:Nginx 负责 HTTPS 解析,后端可用 HTTP 简化配置。
  • 统一入口:对外提供单一域名,后端可以有多种服务(微服务架构场景)。

🔗 Nginx 反向代理配置示例

server {
    listen 80;
    server_name www.example.com;

    location / {
        proxy_pass http://backend_servers;   # 定义上游服务器组
        proxy_set_header Host $host;         # 传递原始Host
        proxy_set_header X-Real-IP $remote_addr;  # 传递真实IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

upstream backend_servers {
    server 192.168.1.100:8080;
    server 192.168.1.101:8080;
}
  • 当客户端访问 www.example.com,Nginx 根据 proxy_pass 把请求转发给 upstream 中定义的 2 台服务器,实现负载均衡。

⚙️ Nginx 如何实现反向代理的底层原理?

  • 事件驱动(异步)模型:Nginx 使用 epoll/kqueue 等机制实现高并发处理,代理转发请求时不会阻塞主线程。
  • 内核高效 I/O:Nginx 在代理转发中使用零拷贝技术(如 sendfile),减少内核态和用户态的切换,转发时性能极高。
  • 模块化设计:反向代理能力来自 ngx_http_proxy_module 模块,灵活配置 proxy_pass、proxy_set_header 等指令。

✅ 应用场景

✔ 网站或 API 服务统一入口:前端请求通过 Nginx 反向代理到不同微服务
✔ 多服务器负载均衡:对接多台后端服务器,平滑分流
✔ HTTPS 加速与安全:由 Nginx 处理 SSL 协议,后端只需要 HTTP
✔ 静态资源加速:Nginx 对静态文件提供缓存能力


🔒 与正向代理的区别

类型代理对象客户端视角使用场景
正向代理客户端客户端主动使用代理访问外网科学上网、访问被封锁网站
反向代理服务器客户端无感知,只知道代理服务器域名负载均衡、隐藏服务器、SSL 终止

🎯 场景:你的网站需要负载均衡

假设你有一个域名:

https://www.myapp.com

你的后端服务有两台机器:

  • 192.168.1.101:8080
  • 192.168.1.102:8080

这两台机器跑着相同的 Web 应用程序,你希望:
✅ 所有用户访问 www.myapp.com 时,都通过 Nginx
✅ Nginx 自动把请求平均分配到后端的两台服务器(负载均衡)
✅ 用户看不到真实的后端地址


🚀 请求过程举例

1️⃣ 用户在浏览器输入:

https://www.myapp.com/home

2️⃣ 浏览器会向 DNS 请求域名解析到 IP,比如:

www.myapp.com --> 公网IP 1.2.3.4

3️⃣ 1.2.3.4 其实是 Nginx 服务器的公网 IP。

4️⃣ Nginx 收到请求 /home 后,根据配置:

upstream my_backend {
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

server {
    listen 80;
    server_name www.myapp.com;

    location / {
        proxy_pass http://my_backend;     # 转发到后端
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;  # 把客户端真实 IP 传给后端
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

5️⃣ Nginx 这里配置了 my_backend upstream,里面有两台后端服务器。

6️⃣ 当 Nginx 接收到 /home 请求时:

  • 会按轮询的方式把请求转发给 192.168.1.101:8080 或 192.168.1.102:8080
  • 后端处理完后响应 HTML 页面给 Nginx
  • Nginx 再把响应转发给用户

7️⃣ 用户只知道响应来自 www.myapp.com,对后端服务器 IP 完全不感知。


✅ 关键点

  • 客户端只和 Nginx 通信:客户端无法直接访问后端,安全性更高。
  • Nginx 在中间做负载均衡:请求均匀分配给多台后端,后端压力平衡。
  • 隐藏后端服务器:后端 IP 不暴露在公网上。
  • 还能做缓存:比如静态资源 JS/CSS 可直接由 Nginx 缓存后返回。

🔎 一个完整的示例流程

假设:

  • 用户 A、B、C 先后访问同一个 URL:

    • A --> Nginx --> 后端服务器 1
    • B --> Nginx --> 后端服务器 2
    • C --> Nginx --> 后端服务器 1
      (轮询策略)
  • 用户始终访问 www.myapp.com,不用关心后端有多少台服务器,也不用改变请求地址。


🔥 为什么这样好?

  • 对客户端:

    • 访问简单:只要域名,不需要记 IP
    • 延迟更低:Nginx 可以缓存静态资源
  • 对运维:

    • 后端随时扩容或替换机器,只需要在 nginx.conf 改一下 upstream
    • 后端不暴露公网,避免直接攻击
  • 对性能:

    • 高并发场景下,Nginx 高效转发,防止后端过载

✅ 通过这个例子,希望你能直观理解:

Nginx 反向代理就是帮你把用户的请求转发到后端服务器,同时提供负载均衡、隐藏后端、缓存、SSL 等功能,它是现代高性能网站架构的核心!

继续解释

1)location / 的含义

  • 在 Nginx 中,location / 表示匹配所有以 / 开头的路径,也就是 所有请求都会进入这个 location
  • 所以像 /home/api/data/about 等等路径,都会被 location / 捕获到。

2)为什么没有必要写 location /home

  • 因为你想把网站的所有路径统一代理到后端服务,而不是只代理 /home
  • 如果你写了 location /home,只会代理 /home 路径及其子路径,比如 /home/page,其他如 /about/contact 就不会被这条规则处理,会返回 404 或命中其他 location。

3)请求示例
假设你的 Nginx 监听 www.myapp.com

  • 当用户访问 http://www.myapp.com/home

    • 浏览器请求路径是 /home

    • 因为 /home/ 开头,所以匹配到 location /

    • 于是 Nginx 把请求 /home 转发给后端:

      proxy_pass http://my_backend;  # 后端接收到的请求路径也是 /home
      
  • 当用户访问 http://www.myapp.com/api/user

    • 浏览器请求路径是 /api/user
    • 也同样匹配到 location /,然后转发。

4)如何只代理 /home 路径
如果你只想把 /home 请求代理到后端,其他路径不转发,可以写:

location /home {
    proxy_pass http://my_backend;
    ...
}

这样:

  • /home/home/xxx 会被转发。
  • /api/about 等不会命中 /home location,会继续往下匹配其他 location 块,或者直接返回 404。

5)保持请求路径的关键
proxy_pass 有一个容易忽略的坑:

  • 当你写 proxy_pass http://my_backend; 并且 location 是 location /,会将请求路径原样传给后端。

    • /home 请求依然是 /home 到后端。
  • 但是如果你写了:

    location /home {
        proxy_pass http://my_backend/;
    }
    

    Nginx 会把匹配到的 /home 部分 剥离掉,把后面的路径拼接到 / 后转发给后端。例如:

    • 用户请求 /home/page -> 后端收到 /page(丢了 /home)。
  • 这种行为经常引发“请求路径错位”的问题。


location /ws/ {
    proxy_pass http://my_backend/ws/;
    ...
}

✅ 1)location /ws/ 匹配范围

  • location /ws/ 表示会匹配所有以 /ws/ 开头的请求路径:

    • /ws/home ✅ 匹配
    • /ws/page ✅ 匹配
    • /ws ❌ 不匹配!注意 /ws(没有结尾斜杠)不会命中 /ws/,会去找是否有别的 location(比如 location /ws),否则匹配不到就落到更上层 location(如 location /)。

✅ 2)proxy_pass 拼接规则

Nginx 的 proxy_pass 在有 路径 时有一个非常重要的规律:

如果 proxy_pass 后面带了路径部分,就会丢弃 location 块里匹配到的路径,并把请求的 URI 中 location 块后面的部分拼接到 proxy_pass 路径上。

来看你的例子:

location /ws/ {
    proxy_pass http://my_backend/ws/;
}
  • 这里的 location 是 /ws/,proxy_pass 是 http://my_backend/ws/

  • 比如用户请求:/ws/home

    • /ws/ 是 location 匹配到的部分
    • /home 是请求中 location 后剩下的部分
  • 按规则:

    最终请求 = proxy_pass 中的路径部分 + location 后面的部分
    

    也就是:

    最终请求 = /ws/ + home = /ws/home
    

✅ 所以你的例子中:

  • 用户请求 /ws/home

  • 匹配到 location /ws/

  • /ws/ 后的部分是 /home

  • 拼接到 proxy_pass 中的 /ws/ 后:

    最终请求 = /ws/ + home = /ws/home
    
  • 后端实际收到的请求是:

    http://192.168.1.101:8080/ws/home
    

    (其中 my_backend 会解析到 upstream 配置里的服务器地址)


✅ 3)为什么很多人会踩坑?

因为很多人会觉得:

我 location 和 proxy_pass 都写了 /ws/,是不是就会造成 /ws/ws/...

但实际上,Nginx 在有路径的 proxy_pass 时会丢弃匹配的 location 部分(/ws/),只拼接剩下的 URI 到 proxy_pass 的路径。


✅ 4)那什么时候会出现路径错位?

如果你写:

location /ws/ {
    proxy_pass http://my_backend;  # 注意,这里proxy_pass没有路径
}

因为这里 proxy_pass 没有路径部分,那么 Nginx 就会把 整个原始请求 URI(不丢弃 location)直接转发给后端。

例如:

  • 请求 /ws/home
  • 最终后端收到 /ws/home

🔎 关键区别总结:

  • proxy_pass 带路径:丢弃 location 匹配到的部分,拼接剩余 URI 到 proxy_pass 路径
  • proxy_pass 不带路径:保留完整的原始请求 URI,原样转发

🔎 总结一句话:

  • 你配置 location / 是想把网站所有路径都代理到后端,/home 属于 / 匹配范围,所以当然能正常转发到后端;
  • 如果只需要代理部分路径,才需要写 location /home
  • 不要被 location 块的语法迷惑,/ 是 Nginx 中“捕获所有路径”的通用方式。

🎯 总结一句话

反向代理就是:Nginx 作为中间人,接收客户端请求,转发给后端,并把响应返回给客户端,同时提供负载均衡、安全、缓存等能力,是现代网站和微服务架构的核心组件。