Nginx反向代理之域名轉發(fā)與路徑重寫配置指南
引言
在現(xiàn)代互聯(lián)網(wǎng)架構中,反向代理早已不再是“可選技能”,而是構建高可用、高性能、安全穩(wěn)定服務的核心基石。Nginx 作為最流行的反向代理服務器之一,憑借其輕量、高效、模塊化的設計,成為無數(shù)企業(yè)級應用的首選。然而,許多開發(fā)者對 Nginx 的理解仍停留在“簡單轉發(fā)”層面——proxy_pass http://localhost:8080; 就是全部。殊不知,真正的力量在于精準控制流量的流向與形態(tài):如何根據(jù)域名動態(tài)路由?如何將 /api/v1/user 透明重寫為 /user-service/api/v1/user?如何在不修改后端代碼的前提下,實現(xiàn)多租戶、灰度發(fā)布、API 網(wǎng)關式管理?
本文將帶你深入 Nginx 反向代理的進階世界,系統(tǒng)性地解析域名轉發(fā)與路徑重寫兩大核心機制。我們將從基礎語法講起,逐步過渡到復雜場景實戰(zhàn),結合真實 Java 后端服務示例,展示如何通過 Nginx 實現(xiàn)無侵入式架構升級。你將學會如何用幾行配置,讓一個原本只能訪問 http://app.example.com:8080 的 Spring Boot 服務,對外表現(xiàn)為 https://api.yourcompany.com/v1/users,同時還能自動處理負載均衡、SSL 終止、緩存策略和安全頭注入。
無論你是運維工程師、后端開發(fā)者,還是全棧架構師,掌握這些技能都將極大提升你對系統(tǒng)流量的掌控力。準備好了嗎?讓我們開啟這場從“能用”到“優(yōu)雅”的進階之旅
一、反向代理基礎:理解 Nginx 的流量入口
在深入域名轉發(fā)與路徑重寫之前,我們必須先厘清一個根本問題:Nginx 是如何“代理”請求的?
想象一下,你的用戶通過瀏覽器訪問 https://api.example.com/users。這個請求首先抵達的是 Nginx 服務器(監(jiān)聽 443 端口),而非你的 Java 應用(運行在 8080 端口)。Nginx 接收請求后,根據(jù)配置規(guī)則,決定“把這個問題交給誰處理”——可能是本地的 Tomcat,也可能是遠在 AWS 的微服務集群。這個“中間人”角色,就是反向代理的本質(zhì)。
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;:監(jiān)聽 HTTPS 請求server_name api.example.com;:匹配請求頭中的 Host 字段,確保只有訪問該域名的請求才會被處理location / { ... }:匹配所有路徑(根路徑)proxy_pass http://localhost:8080;:將請求轉發(fā)到本地 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 轉發(fā)為: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 轉發(fā)為: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 轉發(fā)為:http://backend:8080/service/v1/users ?(看似正常)
但請求:https://api.example.com/api(無尾部)
→ Nginx 轉發(fā)為:http://backend:8080/service ?
問題來了:如果后端服務期望 /api 作為路徑前綴,而你卻在 proxy_pass 中重寫了它,那么后端代碼中 @RequestMapping("/api") 的路徑將永遠無法匹配,因為請求到達時已經(jīng)變成了 /service。
最佳實踐:
- 如果
location以/結尾 →proxy_pass也以/結尾 - 如果
location不以/結尾 →proxy_pass也不應帶路徑 - 強烈建議:統(tǒng)一使用帶
/的寫法,語義清晰,避免歧義
1.3 Java 后端如何感知真實請求?
Nginx 作為代理,會“隱藏”客戶端的真實 IP、協(xié)議、主機名。如果你的 Java 應用(如 Spring Boot)需要記錄訪問日志、做權限校驗、生成完整 URL,就必須依賴 Nginx 傳遞的頭部信息。
@RestController
@RequestMapping("/api/v1")
public class UserController {
@GetMapping("/users")
public ResponseEntity<String> 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 可能經(jīng)過多層代理(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 自動識別這些頭,無需手動解析。但理解其原理,對排查問題至關重要。
二、域名轉發(fā):一個 Nginx 實例,服務多個業(yè)務域
現(xiàn)實世界中,很少有系統(tǒng)只服務一個域名。你可能有:
api.yourcompany.com→ 提供 REST APIadmin.yourcompany.com→ 管理后臺static.yourcompany.com→ 靜態(tài)資源legacy.yourcompany.com→ 舊系統(tǒng)遷移中
Nginx 的 server 塊可以定義多個,每個 server_name 對應一個獨立的虛擬主機。這是實現(xiàn)多租戶、多服務統(tǒng)一入口的核心手段。
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. 靜態(tài)資源(可緩存)
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 測試你的請求頭是否正確傳遞。只需用瀏覽器訪問該鏈接,它會返回你發(fā)送的所有 HTTP 頭信息,包括 Host、X-Forwarded-* 等。
2.2 通配符域名與子域名泛解析
假設你的系統(tǒng)支持多租戶,每個租戶都有獨立子域名:
tenant1.yourcompany.comtenant2.yourcompany.com*.yourcompany.com
Nginx 支持正則表達式和通配符:
server {
listen 443 ssl;
server_name ~^(?<tenant>.+)\.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<String, Object>> 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"));
}
// 根據(jù) tenantId 查詢數(shù)據(jù)庫,返回租戶專屬數(shù)據(jù)
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 最強大的功能之一。它允許你在請求被轉發(fā)前,動態(tài)修改請求的 URI,而用戶和瀏覽器對此完全無感知。
3.1 使用 rewrite 指令:基礎語法
rewrite regex replacement [flag];
regex:正則表達式,匹配請求路徑replacement:替換后的路徑flag:可選標志,如last、break、redirect、permanent
示例 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
→ 轉發(fā)到:http://backend:8080/new-api/v1/users/123
注意:rewrite 發(fā)生在 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 實現(xiàn)更優(yōu)雅的重寫
有時,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匹配階段就捕獲了路徑參數(shù)((.*)) proxy_pass直接拼接,無需額外的rewrite指令- 避免了
rewrite的額外處理開銷
性能對比:在高并發(fā)場景下,location + proxy_pass 比 rewrite 快 15%-20%,因為減少了正則引擎的二次匹配。
3.3 實戰(zhàn)案例:將前端 SPA 路由轉發(fā)到后端 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;
# 靜態(tài)資源:前端打包文件
location / {
root /var/www/frontend;
try_files $uri $uri/ /index.html;
}
# API 路徑重寫:所有 /api/* 轉發(fā)到后端
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 | 支付回調(diào) |
你可以用多個 location 實現(xiàn)精準路由:
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;
}
}每個服務都有獨立的路徑空間,互不干擾,易于維護。后端服務無需關心“全局路徑前綴”,只專注自己的業(yè)務路徑。
四、高級技巧:條件路由、負載均衡與動態(tài)代理
Nginx 的強大遠不止靜態(tài)配置。它支持基于請求頭、Cookie、IP、變量的條件判斷,實現(xiàn)智能路由。
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 → 轉發(fā)到
web-backend - 否則(如手機瀏覽器)→ 轉發(fā)到
mobile-backend
你可以為移動端部署輕量版 API,減少 JSON 字段、關閉日志、啟用壓縮,提升體驗。
4.2 基于 Cookie 的灰度發(fā)布(A/B 測試)
假設你正在發(fā)布新版本的用戶服務,想讓 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,即可實現(xiàn)無感知灰度發(fā)布。適用于金融、電商等高敏感場景。
4.3 負載均衡:多實例自動分發(fā)
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; # 備用節(jié)點
}
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 個發(fā)給 10.0.0.10,1 個發(fā)給 10.0.0.11backup:只有當主節(jié)點全掛時才啟用
?? Nginx 支持多種負載均衡算法:round-robin(默認)、least_conn(最少連接)、ip_hash(會話保持)
upstream user-service {
ip_hash; # 同一 IP 的請求總是發(fā)給同一臺后端
server 10.0.0.10:8080;
server 10.0.0.11:8080;
}ip_hash 適用于需要會話保持的場景(如購物車、登錄態(tài)),但犧牲了負載均衡的均勻性。
4.4 動態(tài)上游:結合 Lua 或外部服務(進階)
雖然 Nginx 本身不支持動態(tài) DNS 查詢,但可通過 ngx_http_lua_module 實現(xiàn):
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 環(huán)境,適合高階用戶。普通場景建議使用 Consul + nginx-upsync-module 實現(xiàn)動態(tài)上游更新。
五、Java 后端實戰(zhàn):如何配合 Nginx 構建現(xiàn)代化網(wǎng)關
現(xiàn)在,我們來構建一個完整的 Java 微服務 + Nginx 反向代理系統(tǒng)。
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<String, Object> getUserInfo(
HttpServletRequest request,
@RequestHeader(value = "X-Tenant-ID", required = false) String tenantId) {
Map<String, Object> 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<String, String> health() {
Map<String, String> 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 配置:統(tǒng)一入口 + 路徑重寫 + 多域名
# 定義上游組
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/* 請求轉發(fā)到后端
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;
}
# 健康檢查端點(不經(jīng)過重寫)
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 測試驗證
- 啟動 Java 服務:
java -jar gateway-demo.jar - 訪問:
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"
}? 注意:originalHost 是 api.example.com,而非 localhost:8080,說明 Nginx 成功傳遞了原始域名。
- 訪問:
https://admin.example.com/api/v1/user
你會看到:404 —— 因為后端服務沒有 /api/v1/user 路徑(它只在 api.example.com 下暴露)
這正是我們想要的:不同域名暴露不同 API,實現(xiàn)邏輯隔離!
六、性能優(yōu)化與安全加固:生產(chǎn)環(huán)境必看
6.1 緩存靜態(tài)內(nèi)容
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 個請求
- 允許突發(fā) 20 個(burst)
nodelay:不延遲,直接拒絕超出部分
6.3 防止惡意請求頭
if ($http_user_agent ~* "sqlmap|nmap|nikto") {
return 403;
}
if ($http_referer ~* "(spam|porn|gambling)") {
return 403;
}可結合 nginx-module-vts 或 ModSecurity 做更深度的 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;
通??蓽p少 70% 的響應體積,尤其對 JSON API 效果顯著。
七、故障排查與日志分析
7.1 查看 Nginx 配置是否正確
nginx -t
輸出 syntax is ok 和 test 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)
一旦后端慢,你就能立即判斷是網(wǎng)絡問題、后端處理慢,還是數(shù)據(jù)庫慢!
八、架構演進:從 Nginx 到服務網(wǎng)格
隨著微服務規(guī)模擴大,Nginx 的配置會變得復雜難維護。此時,你可以考慮:
| 方案 | 適用場景 | 優(yōu)勢 | 劣勢 |
|---|---|---|---|
| Nginx | 小中型系統(tǒng),單一入口 | 配置簡單、性能極高、資源消耗低 | 無服務發(fā)現(xiàn)、無熔斷、無追蹤 |
| Spring Cloud Gateway | Java 技術棧統(tǒng)一 | 深度集成 Spring 生態(tài)、支持斷路器、重試 | JVM 開銷大、不適合靜態(tài)資源 |
| Envoy / Istio | 大型云原生系統(tǒng) | 服務網(wǎng)格、mTLS、流量鏡像、金絲雀發(fā)布 | 學習曲線陡峭、運維復雜 |
建議路徑:
小團隊 → Nginx 精細化配置
中型團隊 → Nginx + Prometheus + Grafana 監(jiān)控
大型團隊 → Istio + K8s + Linkerd
九、總結:你掌握的,不只是配置,是架構話語權
通過本文,你已經(jīng)掌握了:
? Nginx 反向代理的核心原理
? 域名轉發(fā)的多租戶實現(xiàn)
? 路徑重寫的四種優(yōu)雅寫法
? Java 后端如何感知真實請求
? 負載均衡、灰度發(fā)布、限流、安全頭
? 生產(chǎn)環(huán)境最佳實踐與故障排查
更重要的是,你理解了:
Nginx 不是“轉發(fā)工具”,它是你的“流量指揮官”。
它讓你能在不改動一行后端代碼的前提下,完成:
- 多域名隔離
- API 版本平滑遷移
- 租戶路由
- 灰度發(fā)布
- 安全加固
- 性能優(yōu)化
這,就是架構師的底氣。
十、延伸思考:如果你是 CTO,你會怎么設計?
想象你負責一個年交易額 10 億的電商平臺,有:
- 10 個微服務(用戶、商品、訂單、支付、庫存、推薦、風控、物流、通知、后臺)
- 3 個前端(Web、App、小程序)
- 5 個地域數(shù)據(jù)中心
- 每天 5000 萬次 API 調(diào)用
你會如何設計 Nginx 層?
建議架構:
[全球用戶]
↓
[CDN 緩存靜態(tài)資源]
↓
[邊緣 Nginx(全球 10 節(jié)點)]
↓
[區(qū)域 Nginx(北京/上海/廣州)]
↓
[服務網(wǎng)關(Spring Cloud Gateway)]
↓
[微服務集群(K8s)]
- CDN:緩存圖片、CSS、JS
- 邊緣 Nginx:做 DDOS 防護、IP 黑名單、SSL 終止
- 區(qū)域 Nginx:做地域路由(如華北用戶走華北機房)
- 網(wǎng)關:做鑒權、限流、日志、熔斷
- 微服務:專注業(yè)務邏輯
Nginx 在這里,是流量的過濾器、加速器、調(diào)度器,不是簡單的代理。
結語:讓配置成為藝術
配置文件不是冰冷的文本,它是你系統(tǒng)架構的藍圖,是你與流量對話的語言。
當你能用一行 proxy_pass,讓千萬用戶無感知地切換后端服務;
當你能用一個 rewrite,讓舊系統(tǒng)優(yōu)雅退場;
當你能用一個 map,讓 A/B 測試毫秒級生效——
你,就不再是“寫代碼的人”,而是系統(tǒng)架構的設計師。
Nginx 的力量,不在于它能做什么,而在于它讓你不用做什么。
你無需改 Java 代碼,無需重啟服務,無需發(fā)布新版本,就能完成一次全局流量重構。
這就是反向代理的終極魅力。
附錄:常用 Nginx 指令速查表
| 指令 | 用途 |
|---|---|
proxy_pass | 轉發(fā)請求到后端 |
proxy_set_header | 設置轉發(fā)頭 |
rewrite | 重寫 URI |
location | 匹配請求路徑 |
upstream | 定義后端集群 |
limit_req | 限流 |
gzip | 啟用壓縮 |
expires | 設置緩存 |
return | 返回 HTTP 狀態(tài)碼 |
map | 變量映射(強大!) |
try_files | 優(yōu)先查找本地文件 |
以上就是Nginx反向代理之域名轉發(fā)與路徑重寫配置指南的詳細內(nèi)容,更多關于Nginx域名轉發(fā)與路徑重寫配置的資料請關注腳本之家其它相關文章!
相關文章
nginx+lua+redis 灰度發(fā)布實現(xiàn)方案
在微服務化進程中,利用nginx+lua+redis實現(xiàn)灰度發(fā)布至關重要,,通過nginx+lua反向代理,根據(jù)客戶端ip進行路由控制,配合redis存儲允許訪問微服務的ip地址,可以有效地進行用戶分流,感興趣的可以了解一下2024-10-10
nginx封空user_agent實現(xiàn)封禁迅雷的方法
nginx封空user_agent實現(xiàn)封禁迅雷的方法,需要的朋友可以參考下。2010-11-11

