一般 Nginx 會分開監聽 80(HTTP)和 443(HTTPS)。但有些情境只能對外暴露單一 port——例如防火牆限制、或 docker-compose 只映射一個 port——這時需要讓同一個 port 同時接受 HTTP 和 HTTPS 連線。
原理
Nginx 有一個非標準的內部狀態碼 497,意思是「收到了 HTTP 請求,但這個 port 只接受 HTTPS」。透過 error_page 497 攔截並重新導向,就能在同一個 port 上自動把 HTTP 轉成 HTTPS:
1
| error_page 497 301 =307 https://$http_host$request_uri;
|
完整設定範例
以下以 SonarQube 為例,Nginx 在 port 9000 上同時處理 HTTP/HTTPS,並 proxy 到後端服務:
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
| upstream app {
server sonarqube:9000;
}
server {
listen 9000 ssl;
server_name _;
ssl_certificate /etc/ssl/private/server.pem;
ssl_certificate_key /etc/ssl/private/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
client_max_body_size 20M;
gzip on;
gzip_vary on;
gzip_comp_level 1;
gzip_types text/css application/javascript application/json image/png image/webp image/jpeg;
# HTTP 打到 HTTPS port 時,Nginx 回傳 497,透過 error_page 重導向
error_page 497 301 =307 https://$http_host$request_uri;
location / {
proxy_pass http://app;
proxy_set_header Host $http_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 https;
}
}
|
listen 9000 ssl 讓 port 9000 預設只接受 HTTPS。當 HTTP 請求進來時,Nginx 觸發 497 並執行 301 =307 重導向——=307 表示用 307 Temporary Redirect 覆蓋原本的 301,保留請求的 HTTP method。
References