Nginx 反向代理详解

反向代理是 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 会按照你列出的顺序,依次检查文件或目录是否存在(相对于 rootalias 定义的根目录):

  1. 检查文件 :检查 file1 是否存在。
  2. 检查下一个 :如果不存在,检查 file2,以此类推。
  3. 执行回退 :如果前面列出的所有文件都不存在,则执行最后一个参数

关键点:

  • 最后一个参数是回退操作,它必须存在。
  • 前面的参数只是用来"试错"的。

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 块中的 rootalias 指令的。
相关推荐
米高梅狮子13 小时前
第2章 docker容器
运维·docker·云原生·容器·架构·kubernetes·自动化
闵孚龙14 小时前
Claude Code Ultraplan 远程多代理规划全解析:AI Agent、CCR远程容器、异步规划、状态机、计划传送与企业级自动化治理
运维·人工智能·自动化
二宝哥16 小时前
Linux虚拟机网络配置
linux·运维·服务器
卧室小白16 小时前
docker网络与服务编排与集群
运维·docker·容器
陳103016 小时前
Linux:进程间通信 和 简单进程池
linux·运维·服务器
数字化顾问16 小时前
(122页PPT)数字化架构的演进和治理(附下载方式)
java·运维·架构
zt1985q16 小时前
本地部署网页监控工具 Webmonitor 并实现外部访问
运维·服务器·网络·网络协议
匆匆那年96717 小时前
远程 Linux 校园网认证操作手册(本地浏览器法)
linux·运维·服务器
爱喝水的鱼丶18 小时前
SAP-ABAP:ABAP函数 NUMBER_GET_NEXT 详解:从编号范围对象获取下一个编号
运维·数据库·学习·sap·abap
Languorous.18 小时前
Windows 安装 Linux 虚拟机 / WSL 完整教程(新手零失败)
linux·运维·windows