Nginx `proxy_pass` 完全解析:从入门到精通


嘿,各位开发者朋友们!今天我们来聊一个 Nginx 中绝对的“C位”指令——proxy_pass

无论你是刚接触 Nginx 的新手,还是已经玩转各种配置的老手,proxy_pass 都是你绕不开的核心功能。它就像一个神通广大的路由器,能把客户端的请求转发到后台的各种服务上。实现反向代理、负载均衡、微服务网关,都离不开它。

不过,这个指令虽然看起来简单,但里面有不少“坑”,特别是那个让人又爱又恨的斜杠 (/)。别担心,今天这篇文章,我就带你把它彻底搞明白!

什么是 proxy_pass

简单来说,proxy_pass 的作用就是转发请求

它被用在 location 配置块里。当一个请求的 URL 匹配了某个 location 的规则后,Nginx 就会按照这个 location 里的 proxy_pass 指令,把请求原封不动或者稍作修改,然后发送到指定的后端服务器上。

这个过程,我们通常称之为反向代理

来看一个最简单的例子:

server {
    listen 80;
    server_name yourdomain.com;

    # 所有发往 yourdomain.com 的请求
    location / {
        # 都转发到运行在本地 8080 端口的服务上
        proxy_pass http://127.0.0.1:8080;
    }
}

这段配置的意思是,Nginx 监听 80 端口,当有请求访问 yourdomain.com 时,比如 http://yourdomain.com/some/page,Nginx 会把这个请求转发给 http://127.0.0.1:8080/some/page

是不是很简单?别急,魔鬼藏在细节里。

proxy_pass 的核心:URL 末尾的斜杠 (/)

proxy_pass 指令后面跟的 URL 带不带斜杠,会导致完全不同的转发效果。这是 Nginx 新手最容易犯错的地方,我们必须重点掌握!

规则其实就两条:

  1. **不带 /**:Nginx 会将 location 匹配的完整 URI 直接拼接到 proxy_pass 的 URL 后面。
  2. /**:Nginx 会将 location 匹配的 URI 部分从请求中去掉**,然后将剩余部分拼接到 proxy_pass 的 URL 后面。

听起来有点绕?没关系,我们上案例!

假设我们的后端服务地址是 http://127.0.0.1:8080

案例一:proxy_pass 不带斜杠

这个配置就像一个“忠实的搬运工”。

配置:

location /api/ {
    proxy_pass http://127.0.0.1:8080;
}

请求与转发流程:

客户端请求 URL Nginx 转发到后端的 URL 解析
http://yourdomain.com/api/users http://127.0.0.1:8080/api/users location 匹配的 /api/ 和后面的 users 被完整地拼接到了 proxy_pass 的 URL 后面。
http://yourdomain.com/api/products/1 http://127.0.0.1:8080/api/products/1 同样,完整的 URI /api/products/1 被保留并转发。

总结proxy_pass 不带斜杠时,会把 location 块匹配的整个路径(包括 location 自己)都传给后端。

案例二:proxy_pass 带斜杠

这个配置就像一个“聪明的裁剪师”。

配置:

location /api/ {
    proxy_pass http://127.0.0.1:8080/;
}

注意 8080 后面多了一个 /

请求与转发流程:

客户端请求 URL Nginx 转发到后端的 URL 解析
http://yourdomain.com/api/users http://127.0.0.1:8080/users Nginx 将 location 匹配的 /api/ 从原始请求中“砍掉”,只把剩下的 users 拼接到 proxy_pass 的 URL 后面。
http://yourdomain.com/api/products/1 http://127.0.0.1:8080/products/1 同理,/api/ 被移除,只转发了 /products/1

总结proxy_pass 带斜杠时,会先把 location 匹配的那段路径去掉,再把剩下的路径传给后端。这在你想把一个子目录映射到后端服务的根目录时非常有用。

小技巧:你可以这样记:带斜杠,脱一层;不带斜杠,全路径。

进阶玩法与实用技巧

掌握了斜杠的秘密,你就已经超越了 80% 的 Nginx 用户。接下来我们看一些更高级的玩法。

1. 传递真实的用户信息

当请求经过 Nginx 代理后,后端服务直接获取到的 IP 地址会是 Nginx 服务器的 IP,而不是真实用户的 IP。这对于日志记录、风控等场景是致命的。

为了解决这个问题,我们需要手动设置一些请求头,把客户端的真实信息传递给后端。

一个标准的配置如下:

location / {
    proxy_pass http://my_backend;
    
    # 传递真实的用户 IP
    proxy_set_header X-Real-IP $remote_addr;
    
    # 传递 IP 链路,对于经过多层代理的场景很有用
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # 传递原始的 Host 头
    proxy_set_header Host $http_host;
    
    # 允许 WebSocket
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
  • $remote_addr:这是 Nginx 内置变量,代表客户端的 IP 地址。
  • $proxy_add_x_forwarded_for:这个变量会自动把 $remote_addr 追加到 X-Forwarded-For 头的末尾,形成一个 IP 链路。
  • $http_host:代表原始请求的 Host。

把这段配置加到你的 proxy_pass 后面,后端服务就能通过读取 X-Real-IPX-Forwarded-For 头来获取真实的用户 IP 了。

2. 在 proxy_pass 中使用变量

有时候,我们希望转发的地址是动态的。比如,根据请求的某个部分来决定转发到哪个后端服务。这时就可以在 proxy_pass 中使用变量。

案例:根据 URL 的第一部分动态转发

假设我们希望 http://yourdomain.com/user-service/... 转发到 user.backend.comhttp://yourdomain.com/product-service/... 转发到 product.backend.com

server {
    listen 80;
    server_name yourdomain.com;
    
    # DNS 服务器,用于解析变量中的域名
    # 如果你在内网环境,可以设置为你的内网 DNS
    resolver 8.8.8.8;

    location ~ ^/([a-zA-Z0-9-]+-service)/(.*)$ {
        set $service_name $1;
        set $request_path $2;
        
        # 注意:使用变量时,Nginx 不会自动拼接路径
        # 我们需要手动把请求路径 ($request_path) 加上
        proxy_pass http://$service_name.backend.com/$request_path$is_args$args;
    }
}

重点解析:

  1. resolver 指令是必需的:当 proxy_pass 中包含变量时,Nginx 必须在运行时解析域名。你需要指定一个 DNS 服务器地址,否则 Nginx 会报错。
  2. URI 不会自动传递:使用变量后,Nginx 不会像之前那样智能地处理 URI。你需要手动把捕获到的路径($2 在这里就是 $request_path)拼接到 proxy_pass 的 URL 后面。
  3. 参数的传递$is_args$args 是一个固定搭配,$is_args 在有查询参数时值为 ?,否则为空;$args 存储了所有的查询参数。这样可以完美地把原始请求的 query string 转发给后端。

3. 正则表达式 locationproxy_pass

location 使用正则表达式匹配时,proxy_pass 后面不能带有 URI 路径。如果带了,Nginx 会报错。

错误示范:

# 这样会报错!
location ~ ^/api/v(\d+)/ {
    proxy_pass http://127.0.0.1:8080/api/; 
}

正确姿势:
如果你想重写 URL,需要配合 rewrite 指令。

location ~ ^/api/v(\d+)/(.*)$ {
    # 将 /api/v1/users 重写为 /users
    rewrite ^/api/v\d+/(.*)$ /$1 break;
    proxy_pass http://127.0.0.1:8080;
}
  • rewrite ... break;break 标记表示 Nginx 在当前 location 内部完成重写后,就停止后续的匹配,直接执行 proxy_pass
  • 此时 proxy_pass 不带斜杠,它会把重写后的 URI (/users) 直接转发给后端,最终后端收到的请求是 http://127.0.0.1:8080/users

总结

好了,关于 proxy_pass 的核心知识和实用技巧就聊到这里。我们来快速回顾一下关键点:

  1. proxy_pass 是 Nginx 反向代理的核心指令。
  2. URL 末尾的斜杠 (/) 是关键!
    • **不带 /**:转发完整 URI。
    • **带 /**:去掉 location 匹配的路径部分再转发。
  3. 记得用 proxy_set_header 传递真实客户端信息,特别是 X-Real-IPHost
  4. **在 proxy_pass 中使用变量时,必须指定 resolver**,并且需要手动拼接请求 URI 和参数。
  5. 正则表达式 location 中,proxy_pass 不能带路径,需要用 rewrite 指令来配合。

希望这篇文章能帮你彻底搞定 proxy_pass 这个强大的指令。现在,去动手试试吧,你会发现 Nginx 的世界原来如此灵活和有趣!


  目录