🐧 Linux Nginx 反向代理實戰:HTTPS、負載平衡與後端服務整合
在現代的網站與服務架構中,Nginx 反向代理(Reverse Proxy) 幾乎是標準配備。 不論是要統一管理 HTTPS 憑證、在多台後端之間做負載平衡、或將多種服務整合在同一個網域底下, Nginx 都能用簡潔的設定完成。
本篇以 Linux 環境為主,從概念到實戰配置,完整介紹:
- 什麼是「反向代理」?與「正向代理」有什麼不同?
- 如何在 Linux 安裝與啟用 Nginx
- 使用 Nginx 建立 HTTP/HTTPS 反向代理
- 整合 Let's Encrypt 自動簽發免費憑證
- 多台後端服務的負載平衡設定
- 處理 WebSocket、REST API 等特殊情境
- 常見問題與除錯技巧
一、反向代理是什麼?與正向代理的差別
1. 正向代理(Forward Proxy)
正向代理是「站在用戶這一側」的代理伺服器:
- 用戶直接連線到 Proxy,再由 Proxy 替用戶向網路上的網站發出請求。
- 常見用途:公司內部上網控管、翻牆 Proxy、快取外部網站等。
2. 反向代理(Reverse Proxy)
反向代理則是「站在伺服器這一側」:
- 外部用戶只看到反向代理的 IP / 網域。
- 反向代理再根據路徑、Host、Header 等條件,把請求轉發給內部的後端服務。
- 用戶完全不需要知道後端服務真正的 IP 或 Port。
反向代理常見用途包括:
- 集中管理 HTTPS(統一終結 TLS,後端走 HTTP)。
- 多台後端服務的負載平衡(Load Balancing)。
- 將多個應用(API、前端、後台)整合在同一個網域下。
- 作為第一層防線:限制來源 IP、Rate Limit、基本 WAF 規則等。
二、在 Linux 上安裝與啟動 Nginx
以下示例以 Debian/Ubuntu 與 RHEL/CentOS 為主,其它發行版可對應各自的套件管理工具。
1. Debian / Ubuntu
sudo apt update
sudo apt install -y nginx
# 啟動與設定開機自動啟動
sudo systemctl enable --now nginx
# 檢查服務狀態
sudo systemctl status nginx
2. RHEL / CentOS / Rocky / AlmaLinux
sudo dnf install -y nginx # 舊版可用 yum
sudo systemctl enable --now nginx
sudo systemctl status nginx
安裝後,如果防火牆有啟用,記得打開 HTTP/HTTPS:
# 以 firewalld 為例
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
瀏覽器打開 http://伺服器IP,應該就會看到 Nginx 的預設歡迎頁面。
三、基本 Nginx 結構與設定檔位置
常見的 Nginx 目錄結構(以 Debian/Ubuntu 為例):
/etc/nginx/nginx.conf:主設定檔,會 include 其他 conf。/etc/nginx/sites-available/:各站台設定(虛擬主機)。/etc/nginx/sites-enabled/:實際啟用的站台設定(多用 symlink)。/var/www/:預設網站根目錄。/var/log/nginx/access.log、error.log:存放存取與錯誤紀錄。
測試 Nginx 設定檔是否正確:
sudo nginx -t # 檢查語法
sudo systemctl reload nginx # 平滑重新載入設定
四、HTTP 反向代理基本範例
情境:Nginx 對外開放 80 port,後端有一個跑在 127.0.0.1:8080 的 Web 應用程式。
1. 建立站台設定檔
在 /etc/nginx/sites-available/myapp.conf 建立:
server {
listen 80;
server_name example.com;
# 可選擇記錄專用 log
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
location / {
proxy_pass http://127.0.0.1:8080;
# 保留真實 Client IP 與 Host
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. 啟用站台
# 建立 symlink 啟用
sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
# 測試設定並重新載入
sudo nginx -t
sudo systemctl reload nginx
之後使用者只要連到 http://example.com,實際上就是由 Nginx 替他連到 127.0.0.1:8080。
五、HTTPS 與憑證管理:反向代理作為 TLS 終結點
現代網站幾乎都要求使用 HTTPS,加上瀏覽器也會對 HTTP 顯示「不安全」警告。 最典型做法是讓 Nginx 負責 TLS 終結(Terminating TLS),後端維持 HTTP, 如此可集中管理憑證、減少後端負擔。
1. 使用 Let's Encrypt + Certbot 自動簽發憑證
# Debian / Ubuntu
sudo apt install -y certbot python3-certbot-nginx
# 透過 Nginx 外掛自動設定 HTTPS
sudo certbot --nginx -d example.com -d www.example.com
Certbot 會自動幫你:
- 與 Let's Encrypt ACME 伺服器溝通。
- 簽發憑證並存放到
/etc/letsencrypt。 - 修改對應的 Nginx server 區塊,加入
listen 443 ssl;等設定。 - 加入排程(systemd timer)自動續約。
成功後設定檔會長得像這樣(簡化版):
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1: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;
}
}
上面也示範了一個常見做法:所有 HTTP 80 連線一律 301 轉到 HTTPS。
六、多站台整合:同一台 Nginx 代理多個後端服務
在實務中,常常會遇到一台主機上跑很多服務,例如:
- 前端網站
https://www.example.com - API 伺服器
https://api.example.com - 後台管理介面
https://admin.example.com
1. 依「網域」分流(vhost)
可以為不同的 Host 建立不同 server 區塊:
# 前端
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# API
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2. 依「路徑」分流
若只想使用同一個網域,也可以依照 URL 路徑轉發:
server {
listen 443 ssl http2;
server_name example.com;
# /api/* 轉給後端 API 伺服器
location /api/ {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# /admin/* 轉給後台
location /admin/ {
proxy_pass http://127.0.0.1:9000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 其他路徑當作前端網站
location / {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
注意 proxy_pass 後面的「尾端斜線」有語意差異,會影響實際轉發的路徑。
一般情況下,建議「location 以 / 結尾,proxy_pass 也以 / 結尾」,保持一致較不易混亂。
七、負載平衡:多台後端服務分流
當單台後端服務承受不了流量時,就可以在 Nginx 前面加入負載平衡機制, 將請求分散到多台後端伺服器。
1. 設定 upstream 區塊
在 /etc/nginx/nginx.conf 或獨立 conf 裡定義:
upstream myapp_backend {
# 預設是 round-robin
server 10.0.0.11:8080;
server 10.0.0.12:8080;
# 可以設計權重,例如:
# server 10.0.0.11:8080 weight=2;
# server 10.0.0.12:8080 weight=1;
}
2. 在 server 區塊中使用 upstream
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass http://myapp_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;
}
}
3. 常見負載平衡策略
- round-robin(預設):請求輪流分配到各後端。
- least_conn:優先分配給目前連線數最少的後端。
- ip_hash:由 Client IP 決定要連到哪一台,用於需要「會話黏著(sticky)」的服務。
例如:
upstream myapp_backend {
least_conn;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
}
八、特殊情境:WebSocket 與長連線服務
一些現代 Web 應用會使用 WebSocket(例如即時聊天、即時監控), 或是需要長時間保持連線的 SSE(Server-Sent Events)。這類情況需要調整一些 proxy 相關參數。
1. WebSocket 反向代理示例
location /ws/ {
proxy_pass http://127.0.0.1:7000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
2. 調整 Timeout,避免長連線被過早切斷
proxy_read_timeout 3600;
proxy_send_timeout 3600;
實際值可依服務特性調整,避免過短造成連線頻繁重建,過長則可能浪費資源。
九、安全強化與最佳實務
反向代理擺在最前面,自然也是第一層安全防線,建議配合以下做法:
1. 僅開放必要 Port
- 對外只開 80/443,後端服務只聽 localhost 或內網 IP。
- 配合 Linux 防火牆(iptables / nftables / firewalld)限制來源。
2. 嚴格 HTTP Header 與大小限制
# 限制請求體大小,避免被拿來上傳超大檔案
client_max_body_size 10m;
# 加入安全相關 Header(可依情況調整)
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
3. 使用 HTTP/2 與 TLS 強化設定
在 listen 行加上 http2,並採用較新的 TLS 版本與 Cipher。
4. 搭配 Fail2Ban 或 WAF
可透過 log pattern 搭 Fail2Ban 進行簡易防禦,或導入如 ModSecurity 等 WAF 解決方案, 將惡意行為擋在反向代理這一層。
十、常見問題與除錯技巧
1. 502 Bad Gateway / 504 Gateway Timeout
代表 Nginx 與後端服務間的溝通有問題,排查方向包括:
- 後端服務是否有啟動?Port 是否正確?
- Nginx 是否能連到後端 IP(防火牆、SELinux、容器網路…)。
- 超時設定是否過短(
proxy_read_timeout)。
2. 靜態檔案路徑錯亂 / 重複的路徑前綴
通常與 location 與 proxy_pass 的尾斜線有關。
若遇到問題,建議先簡化設定,只處理 / 路徑,再逐步拆出 /api、/admin 等子路徑。
3. 真實 IP 沒有出現在後端 Log
確認是否有設定:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
並在後端 Web Server(例如 Apache、Node.js 應用)中設定使用此 Header, 取代直接讀取 remote_addr。
4. 設定修改後卻沒有生效
- 是否有將站台 conf 放到
sites-enabled(或 include 的目錄)? - 修改後是否有執行
nginx -t與systemctl reload nginx? - 是否有多個相同
server_name的 server 區塊互相衝突?
十一、整理與延伸:把 Nginx 變成你的「統一入口」
從實務角度來看,Nginx 反向代理可以幫你做到:
- 對外只開一組 IP / 網域,內部服務完全藏在後面。
- 集中管理 HTTPS 憑證(尤其搭配 Let's Encrypt 非常方便)。
- 在多台後端之間分散流量,並簡化擴充流程。
- 開始導入更進階的 DevOps 架構,例如容器化、微服務、Kubernetes 等。
當你把常用的反向代理配置整理成 標準模板 之後, 不論是新專案上線、服務搬遷、或臨時增加測試環境, 只要複製一份 conf、調整幾個參數,就能快速完成。
✅ 建議下一步可以搭配:
- Linux 防火牆與 Fail2Ban(限制來源 / 連線次數)。
- 使用 Prometheus + Grafana 監控 Nginx 與後端服務狀態。
- 將 Nginx 反向代理與 Docker / Proxmox 環境整合,作為整個內網服務的「入口閘道」。
沒有留言: