Featured image of post 用單一 Port 同時處理 HTTP 和 HTTPS(Nginx)

用單一 Port 同時處理 HTTP 和 HTTPS(Nginx)

一般 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

Licensed under CC BY-NC-SA 3.0 TW
最後更新 2026-05-07
comments powered by Disqus