Nginx反向代理之域名转发与路径重写配置指南

2026-07-11编程代码238016

在现代互联网架构中,反向代理早已不再是可选技能,而是构建高可用、高性能、安全稳定服务的核心基石,本文将带你深入 Nginx 反向代理的进阶世界,系统性地解析域名转发与路径重写两大核心机制,需要的朋友可以参考下

目录
  • 引言
  • 一、反向代理基础:理解 Nginx 的流量入口
    • 1.1 最基础的 proxy_pass 配置
    • 1.2 路径拼接陷阱:带路径 vs 不带路径
      • 情况一:proxy_pass不带路径
      • 情况二:proxy_pass带路径
      • 情况三:错误组合(不带 / 的 location + 带路径的 proxy_pass)
    • 1.3 Java 后端如何感知真实请求?
    • 二、域名转发:一个 Nginx 实例,服务多个业务域
      • 2.1 多域名基础配置示例
        • 2.2 通配符域名与子域名泛解析
          • 2.3 域名重定向:从 HTTP 到 HTTPS,从旧域到新域
          • 三、路径重写:让后端“看不见”前端路径
            • 3.1 使用 rewrite 指令:基础语法
              • 示例 1:重写/old-api/xxx→/new-api/xxx
              • 示例 2:移除 URL 中的版本号(兼容旧客户端)
            • 3.2 使用 location + proxy_pass 实现更优雅的重写
              • 3.3 实战案例:将前端 SPA 路由转发到后端 API
                • 3.4 复杂路径重写:多级路径映射
                • 四、高级技巧:条件路由、负载均衡与动态代理
                  • 4.1 基于 User-Agent 的路由(移动端 vs PC)
                    • 4.2 基于 Cookie 的灰度发布(A/B 测试)
                      • 4.3 负载均衡:多实例自动分发
                        • 4.4 动态上游:结合 Lua 或外部服务(进阶)
                        • 五、Java 后端实战:如何配合 Nginx 构建现代化网关
                          • 5.1 后端服务:Spring Boot API
                            • 5.2 Nginx 配置:统一入口 + 路径重写 + 多域名
                              • 5.3 测试验证
                              • 六、性能优化与安全加固:生产环境必看
                                • 6.1 缓存静态内容
                                  • 6.2 限制请求速率(防刷)
                                    • 6.3 防止恶意请求头
                                      • 6.4 启用 Gzip 压缩
                                      • 七、故障排查与日志分析
                                        • 7.1 查看 Nginx 配置是否正确
                                          • 7.2 实时查看访问日志
                                            • 7.3 自定义日志格式(记录更多上下文)
                                            • 八、架构演进:从 Nginx 到服务网格
                                              • 九、总结:你掌握的,不只是配置,是架构话语权
                                                • 十、延伸思考:如果你是 CTO,你会怎么设计?
                                                  • 建议架构:
                                                  • 结语:让配置成为艺术
                                                    • 附录:常用 Nginx 指令速查表

                                                      引言

                                                      在现代互联网架构中,反向代理早已不再是“可选技能”,而是构建高可用、高性能、安全稳定服务的核心基石。Nginx 作为最流行的反向代理服务器之一,凭借其轻量、高效、模块化的设计,成为无数企业级应用的首选。然而,许多开发者对 Nginx 的理解仍停留在“简单转发”层面——proxy_pass http://localhost:8080; 就是全部。殊不知,真正的力量在于精准控制流量的流向与形态:如何根据域名动态路由?如何将 /api/v1/user 透明重写为 /user-service/api/v1/user?如何在不修改后端代码的前提下,实现多租户、灰度发布、API 网关式管理?

                                                      本文将带你深入 Nginx 反向代理的进阶世界,系统性地解析域名转发路径重写两大核心机制。我们将从基础语法讲起,逐步过渡到复杂场景实战,结合真实 Java 后端服务示例,展示如何通过 Nginx 实现无侵入式架构升级。你将学会如何用几行配置,让一个原本只能访问 http://app.example.com:8080 的 Spring Boot 服务,对外表现为 https://api.yourcompany.com/v1/users,同时还能自动处理负载均衡、SSL 终止、缓存策略和安全头注入。

                                                      无论你是运维工程师、后端开发者,还是全栈架构师,掌握这些技能都将极大提升你对系统流量的掌控力。准备好了吗?让我们开启这场从“能用”到“优雅”的进阶之旅

                                                      一、反向代理基础:理解 Nginx 的流量入口

                                                      在深入域名转发与路径重写之前,我们必须先厘清一个根本问题:Nginx 是如何“代理”请求的?

                                                      想象一下,你的用户通过浏览器访问 https://api.example.com/users。这个请求首先抵达的是 Nginx 服务器(监听 443 端口),而非你的 Java 应用(运行在 8080 端口)。Nginx 接收请求后,根据配置规则,决定“把这个问题交给谁处理”——可能是本地的 Tomcat,也可能是远在 AWS 的微服务集群。这个“中间人”角色,就是反向代理的本质。

                                                      1.1 最基础的 proxy_pass 配置

                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.example.com;
                                                          ssl_certificate /etc/ssl/certs/api.example.com.crt;
                                                          ssl_certificate_key /etc/ssl/private/api.example.com.key;
                                                          location / {
                                                              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;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }

                                                      这段配置看似简单,实则暗藏玄机:

                                                      • listen 443 ssl;:监听 HTTPS 请求
                                                      • server_name api.example.com;:匹配请求头中的 Host 字段,确保只有访问该域名的请求才会被处理
                                                      • location / { ... }:匹配所有路径(根路径)
                                                      • proxy_pass http://localhost:8080;:将请求转发到本地 8080 端口的 Java 服务

                                                      关键点proxy_pass 后面的 URL 是否包含路径,决定了 Nginx 如何处理原始请求路径!

                                                      1.2 路径拼接陷阱:带路径 vs 不带路径

                                                      这是新手最容易踩的坑!

                                                      情况一:proxy_pass不带路径

                                                      location /api/ {
                                                          proxy_pass http://backend:8080;  # 注意:没有尾部 /
                                                      }

                                                      请求:https://api.example.com/api/v1/users
                                                      → Nginx 转发为:http://backend:8080/api/v1/users

                                                      行为:Nginx 将 location 匹配的部分(/api/原样保留,拼接到目标 URL 后面。

                                                      情况二:proxy_pass带路径

                                                      location /api/ {
                                                          proxy_pass http://backend:8080/service/;  # 注意:有尾部 /
                                                      }

                                                      请求:https://api.example.com/api/v1/users
                                                      → Nginx 转发为:http://backend:8080/service/v1/users

                                                      行为:Nginx 会移除 location 中匹配的路径部分(/api/),然后将剩余部分(v1/users)拼接到 proxy_pass 的路径之后(/service/)。

                                                      情况三:错误组合(不带 / 的 location + 带路径的 proxy_pass)

                                                      location /api {  # 没有尾部 /
                                                          proxy_pass http://backend:8080/service/;  # 有尾部 /
                                                      }

                                                      请求:https://api.example.com/api/v1/users
                                                      → Nginx 转发为:http://backend:8080/service/v1/users ✅(看似正常)

                                                      但请求:https://api.example.com/api(无尾部)
                                                      → Nginx 转发为:http://backend:8080/service

                                                      问题来了:如果后端服务期望 /api 作为路径前缀,而你却在 proxy_pass 中重写了它,那么后端代码中 @RequestMapping("/api") 的路径将永远无法匹配,因为请求到达时已经变成了 /service

                                                      最佳实践

                                                      • 如果 location/ 结尾 → proxy_pass 也以 / 结尾
                                                      • 如果 location 不以 / 结尾 → proxy_pass 也不应带路径
                                                      • 强烈建议:统一使用带 / 的写法,语义清晰,避免歧义

                                                      1.3 Java 后端如何感知真实请求?

                                                      Nginx 作为代理,会“隐藏”客户端的真实 IP、协议、主机名。如果你的 Java 应用(如 Spring Boot)需要记录访问日志、做权限校验、生成完整 URL,就必须依赖 Nginx 传递的头部信息。

                                                      @RestController
                                                      @RequestMapping("/api/v1")
                                                      public class UserController {
                                                          @GetMapping("/users")
                                                          public ResponseEntity getUsers(
                                                                  HttpServletRequest request) {
                                                              String clientIp = request.getHeader("X-Real-IP");
                                                              String forwardedHost = request.getHeader("Host");
                                                              String scheme = request.getHeader("X-Forwarded-Proto");
                                                              System.out.println("Client IP: " + clientIp);
                                                              System.out.println("Original Host: " + forwardedHost);
                                                              System.out.println("Protocol: " + scheme);
                                                              return ResponseEntity.ok("Hello from Java backend!");
                                                          }
                                                      }

                                                      对应的 Nginx 配置必须包含:

                                                      proxy_set_header Host $host;
                                                      proxy_set_header X-Real-IP $remote_addr;
                                                      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                      proxy_set_header X-Forwarded-Proto $scheme;

                                                      为什么需要 X-Forwarded-For
                                                      因为客户端真实 IP 可能经过多层代理(CDN、负载均衡器等),$proxy_add_x_forwarded_for 会追加每一层的 IP,形成链式记录,如:192.168.1.10, 10.0.0.5, 172.16.0.1

                                                      实际项目中,Spring Boot 可通过 server.forward-headers-strategy=native + spring-boot-starter-web 自动识别这些头,无需手动解析。但理解其原理,对排查问题至关重要。

                                                      二、域名转发:一个 Nginx 实例,服务多个业务域

                                                      现实世界中,很少有系统只服务一个域名。你可能有:

                                                      • api.yourcompany.com → 提供 REST API
                                                      • admin.yourcompany.com → 管理后台
                                                      • static.yourcompany.com → 静态资源
                                                      • legacy.yourcompany.com → 旧系统迁移中

                                                      Nginx 的 server 块可以定义多个,每个 server_name 对应一个独立的虚拟主机。这是实现多租户、多服务统一入口的核心手段。

                                                      2.1 多域名基础配置示例

                                                      # 1. API 服务
                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/api.crt;
                                                          ssl_certificate_key /etc/ssl/private/api.key;
                                                          location / {
                                                              proxy_pass http://api-backend: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;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }
                                                      # 2. 管理后台
                                                      server {
                                                          listen 443 ssl;
                                                          server_name admin.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/admin.crt;
                                                          ssl_certificate_key /etc/ssl/private/admin.key;
                                                          location / {
                                                              proxy_pass http://admin-backend:9090;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }
                                                      # 3. 静态资源(可缓存)
                                                      server {
                                                          listen 443 ssl;
                                                          server_name static.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/static.crt;
                                                          ssl_certificate_key /etc/ssl/private/static.key;
                                                          location / {
                                                              root /var/www/static;
                                                              add_header Cache-Control "public, max-age=31536000";
                                                              add_header Access-Control-Allow-Origin "*";
                                                          }
                                                      }

                                                      你可以在 https://httpbin.org/headers 测试你的请求头是否正确传递。只需用浏览器访问该链接,它会返回你发送的所有 HTTP 头信息,包括 HostX-Forwarded-* 等。

                                                      2.2 通配符域名与子域名泛解析

                                                      假设你的系统支持多租户,每个租户都有独立子域名:

                                                      • tenant1.yourcompany.com
                                                      • tenant2.yourcompany.com
                                                      • *.yourcompany.com

                                                      Nginx 支持正则表达式和通配符:

                                                      server {
                                                          listen 443 ssl;
                                                          server_name ~^(?.+)\.yourcompany\.com$;
                                                          ssl_certificate /etc/ssl/certs/wildcard.crt;
                                                          ssl_certificate_key /etc/ssl/private/wildcard.key;
                                                          # 从域名中提取租户 ID,传递给后端
                                                          set $tenant_id $tenant;
                                                          location / {
                                                              proxy_pass http://tenant-service:8080;
                                                              proxy_set_header X-Tenant-ID $tenant_id;  # 自定义头,后端可读取
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }

                                                      在 Java 中,你可以这样获取租户 ID:

                                                      @GetMapping("/dashboard")
                                                      public ResponseEntity<Map> getDashboard(
                                                              HttpServletRequest request) {
                                                          String tenantId = request.getHeader("X-Tenant-ID");
                                                          if (tenantId == null || tenantId.isEmpty()) {
                                                              return ResponseEntity.status(403).body(Map.of("error", "Tenant ID required"));
                                                          }
                                                          // 根据 tenantId 查询数据库,返回租户专属数据
                                                          DashboardData data = dashboardService.findByTenant(tenantId);
                                                          return ResponseEntity.ok(data.toMap());
                                                      }

                                                      这种模式广泛用于 SaaS 平台,如 Notion、Airtable、Salesforce 的多租户架构。无需为每个租户部署独立服务,一个后端实例即可处理所有子域名请求。

                                                      2.3 域名重定向:从 HTTP 到 HTTPS,从旧域到新域

                                                      你可能需要将旧域名迁移到新域名,或强制所有流量走 HTTPS:

                                                      # 强制 HTTP 跳转到 HTTPS
                                                      server {
                                                          listen 80;
                                                          server_name api.yourcompany.com www.api.yourcompany.com;
                                                          return 301 https://$host$request_uri;
                                                      }
                                                      # 旧域名重定向到新域名
                                                      server {
                                                          listen 443 ssl;
                                                          server_name legacy.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/legacy.crt;
                                                          ssl_certificate_key /etc/ssl/private/legacy.key;
                                                          location / {
                                                              return 301 https://api.yourcompany.com$request_uri;
                                                          }
                                                      }

                                                      return 301 是永久重定向,搜索引擎会更新索引,浏览器也会缓存该跳转,提升 SEO 和用户体验。

                                                      三、路径重写:让后端“看不见”前端路径

                                                      路径重写(Rewrite)是 Nginx 最强大的功能之一。它允许你在请求被转发前,动态修改请求的 URI,而用户和浏览器对此完全无感知。

                                                      3.1 使用 rewrite 指令:基础语法

                                                      rewrite regex replacement [flag];
                                                      • regex:正则表达式,匹配请求路径
                                                      • replacement:替换后的路径
                                                      • flag:可选标志,如 lastbreakredirectpermanent

                                                      示例 1:重写/old-api/xxx→/new-api/xxx

                                                      location /old-api/ {
                                                          rewrite ^/old-api/(.*)$ /new-api/$1 last;
                                                          proxy_pass http://backend:8080;
                                                      }

                                                      请求:/old-api/v1/users/123
                                                      → 被重写为:/new-api/v1/users/123
                                                      → 转发到:http://backend:8080/new-api/v1/users/123

                                                      注意:rewrite 发生在 proxy_pass 之前!所以 proxy_pass 只需指向后端的根路径即可。

                                                      示例 2:移除 URL 中的版本号(兼容旧客户端)

                                                      location ~ ^/v[0-9]+/api/ {
                                                          rewrite ^/v[0-9]+/(api/.*)$ /$1 last;
                                                          proxy_pass http://backend:8080;
                                                      }

                                                      请求:/v1/api/users/api/users
                                                      请求:/v2/api/orders/api/orders

                                                      这个技巧在 API 版本迁移中极为实用。你可以在新版本中逐步淘汰 /v1/,而无需强制所有客户端升级。

                                                      3.2 使用 location + proxy_pass 实现更优雅的重写

                                                      有时,rewrite 不是最佳选择。更推荐使用 location 的正则匹配 + proxy_pass 带路径的组合。

                                                      location ~ ^/v[0-9]+/api/(.*)$ {
                                                          proxy_pass http://backend:8080/api/$1;
                                                          proxy_set_header Host $host;
                                                          proxy_set_header X-Real-IP $remote_addr;
                                                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                          proxy_set_header X-Forwarded-Proto $scheme;
                                                      }

                                                      这个写法更清晰、更高效,因为:

                                                      • Nginx 在 location 匹配阶段就捕获了路径参数((.*)
                                                      • proxy_pass 直接拼接,无需额外的 rewrite 指令
                                                      • 避免了 rewrite 的额外处理开销

                                                      性能对比:在高并发场景下,location + proxy_passrewrite 快 15%-20%,因为减少了正则引擎的二次匹配。

                                                      3.3 实战案例:将前端 SPA 路由转发到后端 API

                                                      假设你有一个 Vue.js 前端部署在 https://app.yourcompany.com,后端 API 部署在 https://api.yourcompany.com。但你希望用户只记住一个域名:https://app.yourcompany.com

                                                      于是你这样配置:

                                                      server {
                                                          listen 443 ssl;
                                                          server_name app.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/app.crt;
                                                          ssl_certificate_key /etc/ssl/private/app.key;
                                                          # 静态资源:前端打包文件
                                                          location / {
                                                              root /var/www/frontend;
                                                              try_files $uri $uri/ /index.html;
                                                          }
                                                          # API 路径重写:所有 /api/* 转发到后端
                                                          location /api/ {
                                                              proxy_pass http://backend: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;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }

                                                      前端代码中,你只需写:

                                                      // Vue.js / React 请求
                                                      axios.get('/api/users'); // 实际请求的是 https://app.yourcompany.com/api/users
                                                      // 但 Nginx 会把它转成 http://backend:8080/api/users

                                                      无需跨域配置!无需 CORS!前端和后端“看起来”在同一个域下,完美解决同源策略问题!

                                                      3.4 复杂路径重写:多级路径映射

                                                      想象一个复杂的微服务架构:

                                                      用户请求路径 实际后端服务 说明
                                                      /v1/user/profile user-service:8080/api/v1/profile 用户服务
                                                      /v1/order/list order-service:8080/api/v1/orders 订单服务
                                                      /v1/payment/notify payment-service:8080/webhook/v1/notify 支付回调

                                                      你可以用多个 location 实现精准路由:

                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.yourcompany.com;
                                                          ssl_certificate /etc/ssl/certs/api.crt;
                                                          ssl_certificate_key /etc/ssl/private/api.key;
                                                          # 用户服务
                                                          location ~ ^/v1/user/(.*)$ {
                                                              proxy_pass http://user-service:8080/api/v1/$1;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                          # 订单服务
                                                          location ~ ^/v1/order/(.*)$ {
                                                              proxy_pass http://order-service:8080/api/v1/$1;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                          # 支付服务(特殊路径)
                                                          location ~ ^/v1/payment/notify$ {
                                                              proxy_pass http://payment-service:8080/webhook/v1/notify;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                          # 默认兜底:404
                                                          location / {
                                                              return 404;
                                                          }
                                                      }

                                                      每个服务都有独立的路径空间,互不干扰,易于维护。后端服务无需关心“全局路径前缀”,只专注自己的业务路径。

                                                      四、高级技巧:条件路由、负载均衡与动态代理

                                                      Nginx 的强大远不止静态配置。它支持基于请求头、Cookie、IP、变量的条件判断,实现智能路由。

                                                      4.1 基于 User-Agent 的路由(移动端 vs PC)

                                                      map $http_user_agent $backend {
                                                          default "mobile-backend";
                                                          ~*"(Chrome|Firefox|Safari|Edge)" "web-backend";
                                                      }
                                                      server {
                                                          listen 443 ssl;
                                                          server_name app.yourcompany.com;
                                                          location / {
                                                              proxy_pass http://$backend;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                          }
                                                      }
                                                      • 如果 User-Agent 包含 Chrome/Firefox → 转发到 web-backend
                                                      • 否则(如手机浏览器)→ 转发到 mobile-backend

                                                      你可以为移动端部署轻量版 API,减少 JSON 字段、关闭日志、启用压缩,提升体验。

                                                      4.2 基于 Cookie 的灰度发布(A/B 测试)

                                                      假设你正在发布新版本的用户服务,想让 10% 的用户访问新版。

                                                      map $cookie_gateway_version $upstream {
                                                          default "backend-v1";
                                                          "v2" "backend-v2";
                                                      }
                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.yourcompany.com;
                                                          location /api/v1/user {
                                                              proxy_pass http://$upstream/api/v1/user;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                          }
                                                      }

                                                      然后在前端代码中,随机设置 Cookie:

                                                      // 在用户登录后随机设置
                                                      if (Math.random() < 0.1) {
                                                          document.cookie = "gateway_version=v2; path=/; max-age=3600";
                                                      }

                                                      这种方式无需修改 Nginx 配置,仅通过前端控制 Cookie,即可实现无感知灰度发布。适用于金融、电商等高敏感场景。

                                                      4.3 负载均衡:多实例自动分发

                                                      upstream user-service {
                                                          server 10.0.0.10:8080 weight=3;
                                                          server 10.0.0.11:8080 weight=1;
                                                          server 10.0.0.12:8080 backup;  # 备用节点
                                                      }
                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.yourcompany.com;
                                                          location /api/v1/user {
                                                              proxy_pass http://user-service;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                          }
                                                      }
                                                      • weight=3:每 4 个请求,3 个发给 10.0.0.10,1 个发给 10.0.0.11
                                                      • backup:只有当主节点全挂时才启用

                                                      📈 Nginx 支持多种负载均衡算法:round-robin(默认)、least_conn(最少连接)、ip_hash(会话保持)

                                                      upstream user-service {
                                                          ip_hash;  # 同一 IP 的请求总是发给同一台后端
                                                          server 10.0.0.10:8080;
                                                          server 10.0.0.11:8080;
                                                      }

                                                      ip_hash 适用于需要会话保持的场景(如购物车、登录态),但牺牲了负载均衡的均匀性。

                                                      4.4 动态上游:结合 Lua 或外部服务(进阶)

                                                      虽然 Nginx 本身不支持动态 DNS 查询,但可通过 ngx_http_lua_module 实现:

                                                      location /dynamic-api {
                                                          content_by_lua_block {
                                                              local res = ngx.location.capture("/resolve-backend")
                                                              local backend = res.body
                                                              ngx.req.set_uri("/api/v1/user", false)
                                                              ngx.var.upstream = backend
                                                              ngx.exec("/proxy-to-backend")
                                                          }
                                                      }

                                                      这需要 OpenResty 环境,适合高阶用户。普通场景建议使用 Consul + nginx-upsync-module 实现动态上游更新。

                                                      五、Java 后端实战:如何配合 Nginx 构建现代化网关

                                                      现在,我们来构建一个完整的 Java 微服务 + Nginx 反向代理系统。

                                                      5.1 后端服务:Spring Boot API

                                                      package com.example.gatewaydemo;
                                                      import org.springframework.boot.SpringApplication;
                                                      import org.springframework.boot.autoconfigure.SpringBootApplication;
                                                      import org.springframework.web.bind.annotation.GetMapping;
                                                      import org.springframework.web.bind.annotation.RequestHeader;
                                                      import org.springframework.web.bind.annotation.RestController;
                                                      import javax.servlet.http.HttpServletRequest;
                                                      import java.util.HashMap;
                                                      import java.util.Map;
                                                      @SpringBootApplication
                                                      @RestController
                                                      public class GatewayDemoApplication {
                                                          public static void main(String[] args) {
                                                              SpringApplication.run(GatewayDemoApplication.class, args);
                                                          }
                                                          @GetMapping("/api/v1/user")
                                                          public Map getUserInfo(
                                                                  HttpServletRequest request,
                                                                  @RequestHeader(value = "X-Tenant-ID", required = false) String tenantId) {
                                                              Map response = new HashMap();
                                                              response.put("status", "success");
                                                              response.put("message", "Hello from Java backend!");
                                                              response.put("requestedPath", request.getRequestURI());
                                                              response.put("clientIp", request.getHeader("X-Real-IP"));
                                                              response.put("originalHost", request.getHeader("Host"));
                                                              response.put("tenantId", tenantId);
                                                              response.put("protocol", request.getHeader("X-Forwarded-Proto"));
                                                              return response;
                                                          }
                                                          @GetMapping("/api/v1/health")
                                                          public Map health() {
                                                              Map health = new HashMap();
                                                              health.put("status", "UP");
                                                              health.put("version", "1.2.3");
                                                              return health;
                                                          }
                                                      }

                                                      启动后,访问 http://localhost:8080/api/v1/user,你会看到:

                                                      {
                                                        "status": "success",
                                                        "message": "Hello from Java backend!",
                                                        "requestedPath": "/api/v1/user",
                                                        "clientIp": "127.0.0.1",
                                                        "originalHost": "localhost:8080",
                                                        "tenantId": null,
                                                        "protocol": "http"
                                                      }

                                                      5.2 Nginx 配置:统一入口 + 路径重写 + 多域名

                                                      # 定义上游组
                                                      upstream java-backend {
                                                          server 127.0.0.1:8080;
                                                      }
                                                      server {
                                                          listen 443 ssl;
                                                          server_name api.example.com www.api.example.com;
                                                          ssl_certificate /etc/ssl/certs/api.crt;
                                                          ssl_certificate_key /etc/ssl/private/api.key;
                                                          # 所有 /api/v1/* 请求转发到后端
                                                          location ~ ^/api/v1/(.*)$ {
                                                              proxy_pass http://java-backend/api/v1/$1;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                              # 添加安全头
                                                              add_header X-Content-Type-Options nosniff;
                                                              add_header X-Frame-Options DENY;
                                                              add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
                                                          }
                                                          # 健康检查端点(不经过重写)
                                                          location /health {
                                                              proxy_pass http://java-backend/health;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                          }
                                                          # 默认响应
                                                          location / {
                                                              return 404 "Not Found. Use /api/v1/* endpoints.";
                                                          }
                                                      }
                                                      # 管理后台
                                                      server {
                                                          listen 443 ssl;
                                                          server_name admin.example.com;
                                                          ssl_certificate /etc/ssl/certs/admin.crt;
                                                          ssl_certificate_key /etc/ssl/private/admin.key;
                                                          location / {
                                                              proxy_pass http://java-backend;
                                                              proxy_set_header Host $host;
                                                              proxy_set_header X-Real-IP $remote_addr;
                                                              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                                              proxy_set_header X-Forwarded-Proto $scheme;
                                                          }
                                                      }
                                                      # 强制 HTTPS
                                                      server {
                                                          listen 80;
                                                          server_name api.example.com www.api.example.com admin.example.com;
                                                          return 301 https://$host$request_uri;
                                                      }

                                                      5.3 测试验证

                                                      1. 启动 Java 服务:java -jar gateway-demo.jar
                                                      2. 访问:https://api.example.com/api/v1/user

                                                      你会看到响应:

                                                      {
                                                        "status": "success",
                                                        "message": "Hello from Java backend!",
                                                        "requestedPath": "/api/v1/user",
                                                        "clientIp": "192.168.1.100",
                                                        "originalHost": "api.example.com",
                                                        "tenantId": null,
                                                        "protocol": "https"
                                                      }

                                                      ✅ 注意:originalHostapi.example.com,而非 localhost:8080,说明 Nginx 成功传递了原始域名。

                                                      1. 访问:https://admin.example.com/api/v1/user

                                                      你会看到:404 —— 因为后端服务没有 /api/v1/user 路径(它只在 api.example.com 下暴露)

                                                      这正是我们想要的:不同域名暴露不同 API,实现逻辑隔离!

                                                      六、性能优化与安全加固:生产环境必看

                                                      6.1 缓存静态内容

                                                      location ~* \.(jpg|jpeg|png|gif|ico|css|js|json|woff|woff2)$ {
                                                          root /var/www/static;
                                                          expires 1y;
                                                          add_header Cache-Control "public, immutable";
                                                          add_header Access-Control-Allow-Origin "*";
                                                      }

                                                      减少后端压力,提升前端加载速度。

                                                      6.2 限制请求速率(防刷)

                                                      limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
                                                      server {
                                                          location /api/v1/user {
                                                              limit_req zone=api_limit burst=20 nodelay;
                                                              proxy_pass http://java-backend;
                                                              ...
                                                          }
                                                      }
                                                      • 每秒最多 10 个请求
                                                      • 允许突发 20 个(burst)
                                                      • nodelay:不延迟,直接拒绝超出部分

                                                      6.3 防止恶意请求头

                                                      if ($http_user_agent ~* "sqlmap|nmap|nikto") {
                                                          return 403;
                                                      }
                                                      if ($http_referer ~* "(spam|porn|gambling)") {
                                                          return 403;
                                                      }

                                                      可结合 nginx-module-vtsModSecurity 做更深度的 WAF 防护。

                                                      6.4 启用 Gzip 压缩

                                                      gzip on;
                                                      gzip_vary on;
                                                      gzip_min_length 1024;
                                                      gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

                                                      通常可减少 70% 的响应体积,尤其对 JSON API 效果显著。

                                                      七、故障排查与日志分析

                                                      7.1 查看 Nginx 配置是否正确

                                                      nginx -t
                                                      

                                                      输出 syntax is oktest is successful 才能重启。

                                                      7.2 实时查看访问日志

                                                      tail -f /var/log/nginx/access.log
                                                      

                                                      典型日志格式:

                                                      192.168.1.10 - - [15/Apr/2024:10:23:45 +0800] "GET /api/v1/user HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
                                                      

                                                      7.3 自定义日志格式(记录更多上下文)

                                                      log_format detailed '$remote_addr - $remote_user [$time_local] '
                                                                          '"$request" $status $body_bytes_sent '
                                                                          '"$http_referer" "$http_user_agent" '
                                                                          'rt=$request_time uct="$upstream_connect_time" '
                                                                          'uht="$upstream_header_time" urt="$upstream_response_time" '
                                                                          'host="$host" x-forwarded-for="$http_x_forwarded_for"';
                                                      access_log /var/log/nginx/access.log detailed;

                                                      日志中将包含:

                                                      • 请求耗时(request_time
                                                      • 上游连接时间(upstream_connect_time
                                                      • 上游响应头时间(upstream_header_time
                                                      • 上游响应体时间(upstream_response_time

                                                      一旦后端慢,你就能立即判断是网络问题、后端处理慢,还是数据库慢!

                                                      八、架构演进:从 Nginx 到服务网格

                                                      随着微服务规模扩大,Nginx 的配置会变得复杂难维护。此时,你可以考虑:

                                                      方案 适用场景 优势 劣势
                                                      Nginx 小中型系统,单一入口 配置简单、性能极高、资源消耗低 无服务发现、无熔断、无追踪
                                                      Spring Cloud Gateway Java 技术栈统一 深度集成 Spring 生态、支持断路器、重试 JVM 开销大、不适合静态资源
                                                      Envoy / Istio 大型云原生系统 服务网格、mTLS、流量镜像、金丝雀发布 学习曲线陡峭、运维复杂

                                                      建议路径
                                                      小团队 → Nginx 精细化配置
                                                      中型团队 → Nginx + Prometheus + Grafana 监控
                                                      大型团队 → Istio + K8s + Linkerd

                                                      九、总结:你掌握的,不只是配置,是架构话语权

                                                      通过本文,你已经掌握了:

                                                      ✅ Nginx 反向代理的核心原理
                                                      ✅ 域名转发的多租户实现
                                                      ✅ 路径重写的四种优雅写法
                                                      ✅ Java 后端如何感知真实请求
                                                      ✅ 负载均衡、灰度发布、限流、安全头
                                                      ✅ 生产环境最佳实践与故障排查

                                                      更重要的是,你理解了:

                                                      Nginx 不是“转发工具”,它是你的“流量指挥官”
                                                      它让你能在不改动一行后端代码的前提下,完成:

                                                      • 多域名隔离
                                                      • API 版本平滑迁移
                                                      • 租户路由
                                                      • 灰度发布
                                                      • 安全加固
                                                      • 性能优化

                                                      这,就是架构师的底气。

                                                      十、延伸思考:如果你是 CTO,你会怎么设计?

                                                      想象你负责一个年交易额 10 亿的电商平台,有:

                                                      • 10 个微服务(用户、商品、订单、支付、库存、推荐、风控、物流、通知、后台)
                                                      • 3 个前端(Web、App、小程序)
                                                      • 5 个地域数据中心
                                                      • 每天 5000 万次 API 调用

                                                      你会如何设计 Nginx 层?

                                                      建议架构:

                                                      [全球用户]
                                                           ↓
                                                      [CDN 缓存静态资源]
                                                           ↓
                                                      [边缘 Nginx(全球 10 节点)]
                                                           ↓
                                                      [区域 Nginx(北京/上海/广州)]
                                                           ↓
                                                      [服务网关(Spring Cloud Gateway)]
                                                           ↓
                                                      [微服务集群(K8s)]
                                                      
                                                      • CDN:缓存图片、CSS、JS
                                                      • 边缘 Nginx:做 DDOS 防护、IP 黑名单、SSL 终止
                                                      • 区域 Nginx:做地域路由(如华北用户走华北机房)
                                                      • 网关:做鉴权、限流、日志、熔断
                                                      • 微服务:专注业务逻辑

                                                      Nginx 在这里,是流量的过滤器、加速器、调度器,不是简单的代理。

                                                      结语:让配置成为艺术

                                                      配置文件不是冰冷的文本,它是你系统架构的蓝图,是你与流量对话的语言。

                                                      当你能用一行 proxy_pass,让千万用户无感知地切换后端服务;
                                                      当你能用一个 rewrite,让旧系统优雅退场;
                                                      当你能用一个 map,让 A/B 测试毫秒级生效——

                                                      你,就不再是“写代码的人”,而是系统架构的设计师

                                                      Nginx 的力量,不在于它能做什么,而在于它让你不用做什么
                                                      你无需改 Java 代码,无需重启服务,无需发布新版本,就能完成一次全局流量重构。

                                                      这就是反向代理的终极魅力。

                                                      附录:常用 Nginx 指令速查表

                                                      指令 用途
                                                      proxy_pass 转发请求到后端
                                                      proxy_set_header 设置转发头
                                                      rewrite 重写 URI
                                                      location 匹配请求路径
                                                      upstream 定义后端集群
                                                      limit_req 限流
                                                      gzip 启用压缩
                                                      expires 设置缓存
                                                      return 返回 HTTP 状态码
                                                      map 变量映射(强大!)
                                                      try_files 优先查找本地文件

                                                      以上就是Nginx反向代理之域名转发与路径重写配置指南的详细内容,更多关于Nginx域名转发与路径重写配置的资料请关注北冥有鱼【www.ufcn.cn】其它相关文章!

                                                      您可能感兴趣的文章:

                                                      • Nginx如何设置域名转发到服务器指定的端口
                                                      • nginx配置域名转发到其他域名的几种方法小结
                                                      • nginx多域名转发的实现
                                                      • 解决spring cloud zuul与nginx的域名转发问题
                                                      • Nginx域名转发https访问的实现
                                                      本文地址:https://www.ufcn.cn/article/2432601.html

                                                      如非特殊说明,本站内容均来自于网友自主分享,概不代表本站观点,如有任何问题我们都将在收到反馈后的第一时间进行处理!