VPS 反向代理 Nginx ECH HTTP/2 TLS

Nginx 1.30 把 sticky cookie 從 Plus 拉回開源、預設連後端也改成 HTTP/1.1 keep-alive:ECH、證書壓縮、Early Hints 一次補上反向代理該有的樣子

Nginx 1.30 是 QUIC 併入主線後最有份量的一次 stable,proxy 預設改用 HTTP/1.1 keep-alive、可以對 upstream 直接說 HTTP/2、cookie sticky session 從商業版拉回 OSS,再加上要 OpenSSL 3.5 才能玩的 ECH 與 TLS 證書壓縮。本文拆解每項改動的實務影響與遷移細節。

Nginx 1.30.0 在 2026 年 4 月釋出,是 QUIC/HTTP/3 併進主線之後最有份量的一次 stable 升級。改動集中在反向代理與 TLS 兩塊:proxy 預設連後端改走 HTTP/1.1 keep-alive、upstream 可以直接跟後端說 HTTP/2、cookie 版 sticky session 從商業版 Nginx Plus 拉回開源、OpenSSL 3.5 就緒後可以開 Encrypted ClientHello 與 TLS 證書壓縮。這些不是 changelog 上帶過去的小修,跑在臺灣 VPS 上作為反向代理的 nginx 該不該現在升、每項到底怎麼開才有意義,這篇一項一項拆開講。

預設連後端改成 HTTP/1.1 keep-alive,不動設定也會變

過去 nginx 的 proxy_pass 對後端預設是 HTTP/1.0 且不帶 keep-alive,每一個進來的請求就對後端開一條新 TCP 連線再關掉。這個預設值追溯到 2004 年還沒有 HTTP/1.1 keepalive 生態的時代,之後所有教學都在補丁式地告訴讀者要在 location 裡加上 proxy_http_version 1.1;proxy_set_header Connection "";,再配一個 upstream { keepalive 32; } 才會走連線池。

1.30 把這條預設直接翻過來。裝完新版之後 proxy_pass 對後端就是 HTTP/1.1、Connection 頭預設是空字串、upstream keepalive 模組預設 active。之前寫過補丁的 config 沒事,之前沒寫的 config 現在自動享受連線復用。這對每秒進出幾百個短請求的反向代理是實打實的延遲改善,短連線爆掉的 TIME_WAIT 也會少一大截。

要留意的邊角是後端。有些老舊 CGI、老的 PHP-FPM socket 前端、甚至一些自寫的 upstream 收到 keep-alive TCP 連線會表現得很怪——最經典的是連線閒置一段時間之後後端先關、nginx 下次派請求進去正好撞上 half-close,回一個 502。這種情況 nginx 會自動 retry 到下一個 upstream,但如果 upstream 只有一個而 proxy_next_upstream 沒放行 error,client 就直接看到 502。升上去以後前幾天要看 error log 有沒有 upstream prematurely closed connection,出現就把 keepalive_timeout 調到比後端 idle timeout 短。

HTTP/2 直達 upstream,微服務內網終於不必自己拆頭

同一個版本把 proxy_http_version 2 這條路正式打通。以往 nginx 跟後端之間如果想跑 HTTP/2 只能靠 grpc_pass 走 gRPC 或是自己 patch,一般 REST upstream 進來 HTTP/2、出去強制降成 HTTP/1.1,中間所有多工的好處都被拆光。1.30 讓 upstream 段落可以宣告 HTTP/2,同一條 TCP 連線多路請求,微服務網格內部 kubernetes service 之間或者 nginx 前面接 gRPC 後面接內部 REST 的組合,都可以少一次 protocol downgrade。

實務上真要開 HTTP/2 到 upstream 得盤兩件事。第一是後端必須支援 h2c(純明文的 HTTP/2)或者你願意在內網做 TLS,這件事社群共識非常分歧,但如果 upstream 就在同一臺 VPS 上跑 unix socket 或 loopback,多加 TLS 反而拖慢。第二是 proxy_buffering 開著的時候 HTTP/2 的多工優勢被 nginx 自己的 buffer 吃掉一半,如果 upstream 送 SSE 或 streaming JSON,記得把 buffering 關掉。

sticky cookie 這個功能商業版 Nginx Plus 從 2012 年就有,開源版一直沒給。社群這十幾年只能靠 michaelneale/nginx-sticky-module 這種第三方模組自己編 nginx,或退而求其次用 hash $cookie_JSESSIONID consistent; 之類的方法做偽 sticky——前者要維護 build pipeline,後者遇到 client 不吃 cookie 或換網段就整個 hash 重算,session 直接飛。

1.30 把 cookie 版 sticky 塞進 upstream 模組本體。config 寫起來這樣:

1
2
3
4
5
upstream backend {
server 10.0.0.11:8080;
server 10.0.0.12:8080;
sticky cookie srv_id expires=1h domain=.example.com path=/;
}

nginx 第一次派請求到某臺後端之後,回應會多一個 Set-Cookie: srv_id=<opaque> 給 client,之後所有帶著這顆 cookie 的請求都黏在同一臺 upstream 上。opaque 值是加密過的 upstream identifier,client 看不出來也改不了。這個對於還沒完全 stateless、session 存在記憶體或本機檔的老應用是真的救星,特別是那種要遷雲但沒預算重寫的 legacy Java web。

要提醒的是 sticky learn(NGINX Plus 那個從流量裡挖 session id 的變體)目前還在 Plus,開源版只給 cookie。真的需要 route 或 learn 模式的還是得付錢。另外 sticky 天生跟橫向擴展打架——某臺 upstream 掛掉那顆 cookie 對應的所有 session 就一起飛了,這是 sticky 這個概念本身的限制,nginx 的 config 沒魔法解。

Encrypted ClientHello 進了,但沒 OpenSSL 3.5 開不了

Encrypted ClientHello(ECH)解決 TLS 交握時 SNI 明文外洩這條老問題。原本瀏覽器連 example.com 的時候 SNI 直接寫 example.com 在明文的 ClientHello 裡,中間任何路徑上的 middlebox、ISP、路由器都看得到「這個人在連哪個域名」。ECH 把整個 ClientHello(包含 SNI)封在一個外層 ClientHello 裡面,外層用一個共享的 public SNI,內層要伺服器用私鑰解密才拿得到。

nginx 1.30 的 ECH 需要底層是 OpenSSL 3.5 或以上。這裡有個坑——大部分發行版現在還停在 OpenSSL 3.0 或 3.2,Debian 13 帶的是 3.5,Ubuntu 24.04 是 3.0,RHEL 系的 EL9 也還沒到。要用 ECH 現階段要嘛換 Debian 13、要嘛自己編一個帶新 OpenSSL 的 nginx。config 上開起來就是:

1
2
3
4
5
6
7
8
9
10
server {
listen 443 ssl;
server_name inner.example.com;

ssl_certificate /etc/nginx/certs/inner.crt;
ssl_certificate_key /etc/nginx/certs/inner.key;

ssl_ech_keys /etc/nginx/ech/key1.ech.pem;
ssl_ech_keys /etc/nginx/ech/key2.ech.pem;
}

私鑰塞 nginx,對應的公鑰要發布到 DNS 的 HTTPS resource record 給瀏覽器抓。這條 DNS 依賴讓 ECH 在只有 DNS-over-HTTPS 的環境才真的有意義,如果 DNS 是明文查詢,網路觀察者一樣從 DNS 就看到 client 在解哪個域名,ECH 保護的隱私就漏光。實務上單獨開 ECH 沒什麼用,配 DoH/DoT 一起才成立,多站點共用一個 IP 的環境(同一臺 VPS 上跑很多 SaaS)受益最明顯,那種只有一個域名的個人網站開起來只是負擔。

TLS 證書壓縮跟 Early Hints,兩個延遲小刀

證書壓縮走的是 RFC 8879,Server Hello 之後把整條證書鏈用 brotli 或 zstd 壓一次再送。對 mobile client、高延遲鏈路以及帶完整 chain 的長 fullchain(例如 Let’s Encrypt 的 R11 加上根)節省的 handshake bytes 從 2~4 KB 起跳,對 HTTP/3 那種 1-RTT 完成 TLS 的場景一個 packet 塞不塞得下就是差別。1.30 支援之後只要 client 也支援(現代瀏覽器都會 advertise),nginx 會自動壓縮,不用改 config。

Early Hints(HTTP 103)是另一個路徑。原本 client 送出請求之後要等 server 把完整回應算好才能收到 header,才能開始 preload CSS/JS。有 Early Hints 之後 server 可以先回一個 103 帶 Link: </styles.css>; rel=preload 提示 client 開始拉資源,正式的 200 之後再送。動態頁面(server 端算得慢的、SSR 的、要打資料庫的)節省的 first byte 到 first paint 時間可觀。1.30 讓 nginx 從 upstream 收到 103 之後可以直通 client,不用像以前那樣被 buffering 吞掉。

max_headers 之類的小配件

security-side 補了一個 max_headers directive 限制單一請求最多可以帶多少個 header,預設 100,用來擋 header 灌爆的 DoS。之前只能靠 large_client_header_buffers 間接限制,那個限的是總 bytes 不是 count。另外新增了 $ssl_sigalg$ssl_client_sigalg 兩個變數方便 log 交握時用了哪個簽章演算法,觀察 post-quantum 遷移進度會用到。

該不該升,怎麼升

如果反向代理已經在 stable 1.28 上跑順、後端沒有奇怪的 keep-alive 相容問題,1.30 值得排進近期的維運視窗。預設連後端 keep-alive 這件事升上去馬上有感,sticky cookie 對老 web app 是解決痛點,證書壓縮不用改設定就有。ECH 跟 upstream HTTP/2 屬於「需要另外配套才有意義」的功能,可以之後再排。

升級路徑上,Debian 13 官方 repo 就是新版,直接 apt 就上去;還在 Debian 12 或 Ubuntu 22.04 的話用 nginx 官方 repo 拿 1.30 packages,OpenSSL 版本相依會自己處理。要玩 ECH 才需要在意底層 OpenSSL,其他功能 OpenSSL 3.0 就跑得動。設定檔向後相容,之前手動加的 proxy_http_version 1.1Connection "" 不衝突,可以留著也可以慢慢清乾淨。

NCSE Network 的臺灣是方電訊機房 VPS 適合這種反向代理場景——Intel Gold CPU 對 TLS handshake 的加密運算給得起,NVMe SSD 對 nginx 的 access log 寫入延遲穩定,臺灣境內 latency 讓 sticky session 這種黏著特定後端的架構表現得比跨海方案好。要把 nginx 1.30 這批新特性用起來、或需要一臺乾淨 Debian 13 環境自己編 OpenSSL 3.5 版本試 ECH 的,可以到 ncse.tw 看方案。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →