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 指令的。
相关推荐
^—app5668661 小时前
游戏运存小启动不起来临时解决方法
运维·服务器
Ujimatsu1 小时前
虚拟机安装Debian 13.x及其常用软件(2026.4)
linux·运维·ubuntu
志栋智能2 小时前
超自动化安全:构建智能安全运营的核心引擎
大数据·运维·服务器·数据库·安全·自动化·产品运营
Edward111111114 小时前
4月28日防火墙问题
linux·运维·服务器
想学后端的前端工程师4 小时前
【补充内外网突然不通的情况】
运维·服务器
面汤放盐4 小时前
何时使用以及何时不应使用微服务:没有银弹
java·运维·云计算
子琦啊4 小时前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
AOwhisky5 小时前
Kubernetes 学习笔记:集群管理、命名空间与 Pod 基础
linux·运维·笔记·学习·云原生·kubernetes
小龙在慢慢变强..6 小时前
目录结构(FHS 标准)
linux·运维·服务器