熱門分類
載入中…
目錄0%

🌐 Nginx Reverse Proxy Advanced:Multisite、Rewrite、Caching、Load Balancing 全攻略

    🌐 Nginx Reverse Proxy Advanced:Multisite、Rewrite、Caching、Load Balancing 全攻略

    在現代 Web 架構裡,Nginx Reverse Proxy(反向 Proxy) 幾乎是標配:
    一個對外的入口層,背後可以是多台 Web Server、Application Server、API、微服務、甚至是不同網段中的內部系統。

    很多人只用 Nginx 轉發幾個後端服務就停手,但在實務上,我們常常會遇到:

    • 同一個網域下要服務多個應用:/app1/app2/grafana
    • 同一個 IP 要承載多個網域(Multisite):app.example.comapi.example.com
    • 想用 Rewrite 統一網址結構,避免舊連結 404。
    • 希望前端帶一層 快取與負載均衡,減輕後端壓力。

    本文從概念、架構,到實際設定範例與排錯心法,一次整理 Nginx Reverse Proxy 的進階玩法, 幫你把 Nginx 打造成穩定、可觀測、可擴充的前端入口層。

    📑 目錄

    一、Nginx Reverse Proxy 的角色與核心概念

    Nginx 在入口層主要扮演幾個角色:

    • Reverse Proxy:接收 Client 請求,再幫它轉發到內部服務。
    • 集中 TLS 終結:對外只在 Nginx 做 HTTPS,內部與後端採 HTTP。
    • URL 與 Host Routing:依 Hostname、URL 路徑分流到不同服務。
    • 快取與壓縮:靜態檔案與可快取 API 由前端先擋住大量流量。
    • 負載均衡:一組 upstream 背後可以掛多台後端。

    核心觀念是:對外只看到 Nginx;對內由它掌控一切請求應該去哪裡、以什麼樣的速度與規則送達。

    二、最小可用範例:單一服務 Reverse Proxy

    先從最基本的例子開始:Nginx 接住 https://app.example.com,背後是一台在內部網段的 Web 服務。

    # /etc/nginx/conf.d/app.conf
    server {
        listen 80;
        server_name app.example.com;
    
        # 若已在外部做 TLS 終結,可先用 HTTP;若要 Nginx 終結 TLS 則改為 listen 443 ssl ...
    
        location / {
            proxy_pass http://10.0.10.20:8080;
    
            # 基本 Header 傳遞
            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;
        }
    }
    

    檢查設定並重新載入:

    sudo nginx -t
    sudo systemctl reload nginx
    

    這就是最小可用的 Reverse Proxy 配置,但實務上我們還會想要:

    • 多個網域 / 子網域(Host-Based Multisite)。
    • 同網域下依照路徑分流到不同服務(Path-Based Routing)。
    • 在入口層做快取、負載均衡、Rewrite 舊網址等。

    三、多站點架構:Host-Based 與 Path-Based Routing

    1. Host-Based:多個網域或子網域

    典型用法:一個 Nginx 對應多個服務,各自有不同網域。

    # /etc/nginx/conf.d/app1.conf
    server {
        listen 80;
        server_name app1.example.com;
    
        location / {
            proxy_pass http://10.0.10.21: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;
        }
    }
    
    # /etc/nginx/conf.d/app2.conf
    server {
        listen 80;
        server_name app2.example.com;
    
        location / {
            proxy_pass http://10.0.10.22: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. Path-Based:同網域下切分應用路徑

    另一種常見情境:全部都走 https://portal.example.com,但不同路徑對應不同服務。

    # /etc/nginx/conf.d/portal.conf
    server {
        listen 80;
        server_name portal.example.com;
    
        # /app1 → 轉給內部 10.0.10.31
        location /app1/ {
            proxy_pass http://10.0.10.31: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;
        }
    
        # /app2 → 轉給內部 10.0.10.32
        location /app2/ {
            proxy_pass http://10.0.10.32: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;
        }
    
        # /grafana → Grafana 後端
        location /grafana/ {
            proxy_pass http://10.0.10.40: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;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    注意 location /path/proxy_pass 的結尾斜線關係,會影響 URL 被轉發時的路徑拼接方式, 若處理不當很容易出現 // 或路徑被吃掉的錯誤。建議在實際環境多做幾個對照測試。

    四、Rewrite 與 URL 設計:return / rewrite / try_files

    在做 Reverse Proxy 時,常常要處理舊網址導向、新舊 API 路徑切換、或是讓靜態網站框架(例如 SPA、前端 Router)正常工作, 這時候就會用到 Rewrite 與 Redirect

    1. 用 return 做簡單 301/302 導轉

    # 將舊網址 /old-path 永久導向新網址 /new-path
    location = /old-path {
        return 301 /new-path;
    }
    
    # 將 http 全部導向 https(常見)
    server {
        listen 80;
        server_name app.example.com;
        return 301 https://$host$request_uri;
    }
    

    2. 用 rewrite 做路徑改寫(內部轉向)

    # 將 /api/v1/... 改寫成 /v1/... 再交給後端(不改變瀏覽器位址列)
    location /api/ {
        rewrite ^/api/(.*)$ /$1 break;
        proxy_pass http://10.0.10.50:8080;
    }
    

    break 代表改寫完後在同一個 location 繼續處理,不再重新跑一次 location 匹配。

    3. try_files:靜態檔案與 SPA 的好朋友

    # 典型 SPA 前端設定:不存在的路徑都丟給 index.html
    location / {
        try_files $uri $uri/ /index.html;
    }
    

    為了避免出現 Redirect 迴圈與難以維護的 rewrite 規則, 一般建議的策略是:能用 return 的地方就用 return,只有需要內部改寫時才上 rewrite。

    五、Nginx Proxy Cache:為後端減壓的快取層

    Nginx 可以直接在 Reverse Proxy 層做 HTTP Cache,對於不常變動的 API 或靜態內容來說, 能大幅減少後端壓力與回應時間。

    1. 定義快取區與 Key

    # http 區段(通常在 nginx.conf)
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:100m
                     max_size=10g inactive=60m use_temp_path=off;
    

    說明:

    • keys_zone=my_cache:100m:快取索引儲存在記憶體中,約 100MB。
    • max_size=10g:快取檔案最多使用 10GB 磁碟空間。
    • inactive=60m:60 分鐘沒被訪問的 cache 會被清除。

    2. 在 server / location 啟用 Cache

    server {
        listen 80;
        server_name api.example.com;
    
        location /v1/ {
            proxy_pass http://10.0.20.10:8080;
    
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            add_header X-Proxy-Cache $upstream_cache_status;
    
            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-Proxy-Cache Header(例如 MISS / HIT / EXPIRED), 來判斷快取命中狀況。

    六、Load Balancing:多台後端的流量分散策略

    Nginx 的 upstream 模組可以讓你非常容易把流量分散到多台後端主機。

    1. 基本 round-robin 負載均衡

    # http 區段中定義 upstream
    upstream app_backend {
        server 10.0.30.11:8080;
        server 10.0.30.12:8080;
    }
    
    server {
        listen 80;
        server_name app.example.com;
    
        location / {
            proxy_pass http://app_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;
        }
    }
    

    2. least_conn、ip_hash 等策略

    # 以連線數最少為優先(適合長連線或資源差異較大的後端)
    upstream app_backend_least {
        least_conn;
        server 10.0.30.11:8080;
        server 10.0.30.12:8080;
    }
    
    # 以 client IP 算 hash(簡易 session 黏著)
    upstream app_backend_sticky {
        ip_hash;
        server 10.0.30.11:8080;
        server 10.0.30.12:8080;
    }
    

    若你的應用對 Session 黏著度有要求(例如 Session 存在 local memory), 可以搭配 ip_hash 或改用外部 Session Store(Redis 等),就可以使用更平均的分流策略。

    七、X-Forwarded-* 與 Header 處理:讓後端知道真實世界

    Reverse Proxy 之後,後端看到的 Client IP 會變成 Nginx 的 IP,因此我們通常會透過標準 Header 把真實 Client 資訊帶進去:

    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-Real-IP:記錄最接近 Client 的來源 IP。
    • X-Forwarded-For:一條 Proxy 鏈路上的所有 IP。
    • X-Forwarded-Proto:告訴後端原本是 HTTP 還是 HTTPS。

    很多應用會依據這些 Header 來產生日誌、產生絕對網址、或判斷是否強制跳轉 HTTPS, 如果沒設定好,常出現「在 HTTPS 的前端,但後端以為是 HTTP」之類的怪異行為。

    八、Timeout / Buffer / 限制值:避免莫名斷線與爆記憶體

    在高延遲或後端查詢較慢的環境裡,如果 Proxy timeout 太短,就會看到大量 502/504。 常用參數包括:

    # server 或 location 範圍皆可設定
    proxy_connect_timeout   5s;
    proxy_send_timeout      60s;
    proxy_read_timeout      60s;
    
    # 控制 Nginx 接收上游回應的 buffer
    proxy_buffer_size       16k;
    proxy_buffers           4 32k;
    proxy_busy_buffers_size 64k;
    

    在調整這些數值時,建議先理解後端應用的平均與最長處理時間,再預留合理空間,避免一味拉很長造成資源長時間被占用。

    九、排錯與實戰心法:502/504、迴圈 Redirect、Header 問題

    1. 502 / 504 錯誤

    • 確認後端服務是否真的有在指定 IP/Port 上 listen。
    • 檢查 Nginx error log 裡的詳細錯誤訊息(如 connect() failedupstream timed out)。
    • 調整 proxy_connect_timeoutproxy_read_timeout 等參數。
    • 確認 Security Group / 防火牆是否放行 Nginx 與後端之間的連線。

    2. 無限 Redirect / URL 迴圈

    • 常見於 HTTP→HTTPS、或應用程式內再轉一次 Redirect。
    • 檢查應用是否依據 X-Forwarded-Proto 判斷協定,若沒設好會以為自己在 HTTP 上再轉一次 HTTPS。
    • 簡化 Rewrite 規則,能用 return 就不用複雜的 rewrite 正則。

    3. Client IP 都變成 Nginx 的 IP

    • 確認有設定 X-Real-IPX-Forwarded-For
    • 在後端 Web Server 或應用框架中,設定採用這些 Header 作為真實 IP 的來源。
    • 如果有多層 Proxy,要做信任 Proxy 列表(避免偽造 Header)。

    十、常見問題 FAQ

    Q1. 什麼情況下適合在前端使用 Nginx Reverse Proxy?

    只要你的架構中存在多個後端服務、需要集中控管對外入口、或者希望把 TLS、快取、Rewrite、存取控制集中處理, 幾乎都可以考慮在前端放一層 Nginx Reverse Proxy。特別是當你未來有計畫導入微服務或容器平台時, 這層入口會變得更加關鍵。

    Q2. Nginx Reverse Proxy 和 Load Balancer 有什麼差別?

    Nginx 本身既可以是 Reverse Proxy,也可以是 Load Balancer。概念上:

    • Reverse Proxy:代表後端服務接收請求,處理 URL、Header、Cookie、壓縮、TLS 等邏輯。
    • Load Balancer:專注在把流量分散到多台後端主機上。

    很多實作會直接在同一台 Nginx 上同時做這兩件事,真正的差別比較偏向「架構角色」而非工具本身。

    Q3. 要不要把靜態檔案直接放在 Nginx 上,而不是後端?

    在多數情況下是值得的。將靜態檔案(圖片、CSS、JS、前端打包結果等)直接由 Nginx 提供,可以大幅減少後端負擔, 並且更容易設定 Cache-Control 與壓縮,不過你仍然需要考慮部署流程(如何同步檔案)與版本管理策略。

    Q4. 要不要直接把快取與負載均衡都做在同一台 Nginx 上?

    小型或單一區域的環境,可以用「一台 Nginx 做到底」的方式,維護簡單、成本低; 但在高流量、多地部署或高度關鍵系統中,通常會分層:L4 負載均衡、CDN 或專用快取層、L7 Reverse Proxy,再往後才是應用。

    🧭 行動清單:如何把這些設定落地到你的環境?

    ✅ 先整理現有服務清單:有哪些網域 / 路徑 / 內部 IP / Port?
    ✅ 決定 Multisite 策略:以 Host 為主,還是以 Path 為主,或兩者混用?
    ✅ 在 Lab 或測試環境先搭一層 Nginx Reverse Proxy,驗證 Proxy / Rewrite / Header 行為
    ✅ 對靜態內容或可快取 API 啟用 Proxy Cache,觀察 Hit Rate 與後端負載變化
    ✅ 對負載較重的服務設計 upstream load balancing,搭配 least_conn 或 ip_hash
    ✅ 寫一份簡短的「排錯手冊」,包含常見 502/504、Redirect 迴圈、Client IP 問題的處理步驟
    

    💬 一起分享你的 Nginx Reverse Proxy 架構

    你目前的 Nginx Reverse Proxy 是怎麼設計的?
    是單一前端 Gateway,還是搭配多層 Load Balancer 與 CDN?
    在導入多站點、Rewrite、快取與負載均衡的過程中,有沒有踩過什麼有趣的坑?
    歡迎在下方留言分享你的實戰經驗,也可以提出想看的延伸主題(例如與 Kubernetes Ingress、Service Mesh 的整合), 我會再整理成後續文章 🙌


    — WWFandy・Nginx Reverse Proxy 筆記

    🔗 分享這篇 LINE Facebook X

    沒有留言:

    字級