Ubuntu 22.04 Nginx 反向代理設定

Ubuntu 22.04 Nginx Reverse Proxy Configuration

反向代理(Reverse Proxy)是現代網站架構中不可或缺的元件。本文將詳細介紹如何在 Ubuntu 22.04 上使用 Nginx 設定反向代理,包含 SSL 終止、WebSocket 支援及負載平衡等進階功能。

反向代理概念

什麼是反向代理?

反向代理是一種伺服器,它位於後端伺服器與用戶端之間,代表後端伺服器接收用戶端的請求。與正向代理(Forward Proxy)不同,反向代理是代表伺服器端運作的。

1
用戶端 --> 反向代理 (Nginx) --> 後端伺服器 (Application Server)

反向代理的優點

優點說明
負載平衡將請求分散到多個後端伺服器
SSL 終止在反向代理處理加密,減輕後端負擔
快取快取靜態內容,加速回應速度
安全性隱藏後端伺服器,提供額外防護層
壓縮壓縮回應內容,減少頻寬使用

基本反向代理設定

前置需求

確保已安裝 Nginx:

1
2
sudo apt update
sudo apt install nginx -y

建立基本反向代理設定

假設後端應用程式運行在 localhost:3000,建立設定檔:

1
sudo nano /etc/nginx/sites-available/reverse-proxy

加入以下內容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.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;
    }
}

重要的 Header 設定說明

Header說明
Host傳遞原始請求的主機名稱
X-Real-IP傳遞用戶端的真實 IP 位址
X-Forwarded-For記錄請求經過的所有代理 IP
X-Forwarded-Proto傳遞原始請求的協定(http/https)

啟用設定:

1
2
3
sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

SSL 終止(SSL Termination)

SSL 終止是指在反向代理處理 SSL/TLS 加密與解密,後端伺服器只需處理純 HTTP 請求。

使用 Let’s Encrypt 取得憑證

1
2
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com

手動設定 SSL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # SSL 安全設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_stapling on;
    ssl_stapling_verify on;

    # 安全 Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.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;
    }
}

WebSocket 支援

許多現代應用程式使用 WebSocket 進行即時通訊。Nginx 需要額外設定才能正確代理 WebSocket 連線。

WebSocket 反向代理設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
server {
    listen 443 ssl http2;
    server_name 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://localhost:3000;
        proxy_http_version 1.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;

        # WebSocket 支援
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # WebSocket 超時設定
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }

    # 或者針對特定路徑啟用 WebSocket
    location /ws/ {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }
}

WebSocket 設定說明

設定說明
Upgrade告知伺服器升級協定(從 HTTP 升級到 WebSocket)
Connection "upgrade"指定連線需要升級
proxy_read_timeout讀取超時時間(WebSocket 需要較長時間)

負載平衡簡介

當單一後端伺服器無法應付所有流量時,可以使用 Nginx 的負載平衡功能將請求分散到多個伺服器。

定義 Upstream 群組

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
upstream backend_servers {
    server 192.168.1.10:3000;
    server 192.168.1.11:3000;
    server 192.168.1.12:3000;
}

server {
    listen 443 ssl http2;
    server_name 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://backend_servers;
        proxy_http_version 1.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 提供多種負載平衡策略:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1. Round Robin(預設)- 依序輪流分配
upstream backend_servers {
    server 192.168.1.10:3000;
    server 192.168.1.11:3000;
}

# 2. Least Connections - 分配給連線數最少的伺服器
upstream backend_servers {
    least_conn;
    server 192.168.1.10:3000;
    server 192.168.1.11:3000;
}

# 3. IP Hash - 根據用戶端 IP 分配(Session 黏著)
upstream backend_servers {
    ip_hash;
    server 192.168.1.10:3000;
    server 192.168.1.11:3000;
}

# 4. Weighted - 加權分配
upstream backend_servers {
    server 192.168.1.10:3000 weight=3;  # 接收 3 倍的請求
    server 192.168.1.11:3000 weight=1;
}

健康檢查與備援

1
2
3
4
5
upstream backend_servers {
    server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:3000 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:3000 backup;  # 備援伺服器
}
參數說明
max_fails允許的最大失敗次數
fail_timeout失敗後暫停使用的時間
backup僅在其他伺服器都失敗時使用
down標記伺服器為永久離線

完整設定範例

以下是一個包含所有功能的完整設定範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
upstream backend_servers {
    least_conn;
    server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:3000 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:3000 backup;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL 設定
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # 安全 Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    # 存取日誌
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    # 靜態檔案快取
    location /static/ {
        alias /var/www/example.com/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # API 反向代理
    location /api/ {
        proxy_pass http://backend_servers;
        proxy_http_version 1.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;

        # 超時設定
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # WebSocket 端點
    location /ws/ {
        proxy_pass http://backend_servers;
        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;
        proxy_read_timeout 86400;
    }

    # 預設位置
    location / {
        proxy_pass http://backend_servers;
        proxy_http_version 1.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;
    }
}

常見問題排解

檢查設定語法

1
sudo nginx -t

查看錯誤日誌

1
sudo tail -f /var/log/nginx/error.log

502 Bad Gateway

通常是後端伺服器未啟動或連線被拒絕:

1
2
3
4
5
# 檢查後端服務是否運行
sudo systemctl status your-backend-service

# 檢查連接埠是否在監聽
sudo netstat -tlnp | grep 3000

WebSocket 連線失敗

確認 UpgradeConnection header 設定正確,並檢查防火牆是否允許 WebSocket 連線。

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy