nginx -t 到底驗證了什麼

nginx -t 會解析設定檔、載入憑證與私鑰,並比對兩者的模數是否相符——所以憑證配錯私鑰確實會被擋下來,這點比很多人以為的嚴格。

但它的範圍就到這裡。它不知道你的憑證涵蓋哪些網域、不檢查憑證鏈是否完整、更不會模擬一次真實連線來看使用者最後拿到什麼。它驗證的是「這份設定能不能載入」,不是「這份設定會產生什麼結果」。

你看到的 實際驗證了什麼 沒被涵蓋的部分
nginx -t 顯示 successful 語法正確,憑證與私鑰成對 憑證鏈完整性、網域涵蓋範圍
憑證檔已經是新的 磁碟上的檔案確實更新了 未 reload 前仍供應記憶體中的舊憑證
server_name 設定正確 該網域的請求會命中這個區塊 不匹配的請求會落到 default server
憑證與中繼都在同一個檔案 內容齊全 順序錯誤時 nginx 會取錯那一張
四種設定看似正確、但使用者端仍可能出錯的情況。

下表把四種「看起來沒問題」的狀況與實際落差列出來。第二列是最常見的一種。

三個讓設定與實際脫鉤的機制

以下三種都是設定檔完全正確、但使用者仍會出錯的情況:

  1. 憑證在記憶體裡,不在檔案裡 Nginx 在啟動或重新載入時把憑證讀進記憶體,之後就一直使用那一份。續簽把檔案換成新的並不會改變正在執行的程序——必須送出 reload 訊號。這是「憑證明明更新了卻還是過期警告」最常見的原因,而且因為服務本身完全正常,很難聯想到這一層。
  2. TLS 交握發生在 Nginx 知道網域之前 官方文件講得很直接:SSL 連線在瀏覽器送出 HTTP 請求之前就已建立,此時 Nginx 還不知道對方要的是哪個網域,因此只能先提供 default server 的憑證。SNI 解決了這個問題——瀏覽器在交握階段就帶上網域——但若請求沒有匹配到任何 server_name,仍會落到 default server,拿到那個區塊的憑證。
  3. Nginx 只把檔案裡的第一張憑證當作伺服器憑證 ssl_certificate 指向的檔案可以包含多張憑證,但伺服器憑證必須排在最前面,後面才是中繼憑證。官方文件明確要求這個順序。如果串接時順序顛倒,Nginx 會拿中繼憑證去配對你的私鑰,然後以 key values mismatch 啟動失敗——錯誤訊息指向私鑰,但真正的問題在順序。

default server 為什麼會咬到你

Nginx 在同一個監聽位址上,若沒有任何 server 區塊標記 default_server,第一個區塊就自動成為預設。所以設定檔裡區塊的排列順序,會直接決定「網域不匹配時使用者看到誰的憑證」。

這造成的症狀很容易誤導:某個網域明明設定正確,使用者卻收到「憑證屬於別的網域」的警告。典型情境是新增了一個網域但 DNS 已經指過來、而 server_name 還沒寫對,或是有人直接用 IP 位址連線——這兩種都會落到 default server。

另外要知道 SNI 並非萬無一失。TLS 1.3 要求實作 SNI,現代瀏覽器都會送,但某些舊客戶端、以及直接以 IP 連線的請求不會帶。對這些情況,default server 的憑證就是他們唯一會拿到的東西。

確認使用者實際拿到什麼

所有檢查的原則都一樣:不要看設定檔,要看伺服器實際送出什麼。openssl s_client -connect 網域:443 -servername 網域 會建立一次真實交握並印出憑證,其中 -servername 就是送出 SNI,這一項不能省——省掉它等於在測試「沒有 SNI 的客戶端會拿到什麼」,結果會是 default server 的憑證。

要驗證 reload 是否確實生效,比對憑證的有效期起始時間(notBefore)最直接。如果線上供應的憑證起始時間仍是舊的那一張,代表程序還沒重新載入。這比檢查檔案修改時間可靠,因為檔案早就更新了。

三個層次的設定檢查

依可靠度由低到高:

一、nginx -t 只當作第一道

它能擋下語法錯誤與憑證私鑰不配對,值得每次改完都跑。但通過不代表設定正確,只代表設定能被載入。把它當作必要條件而非充分條件。另外值得順手清掉的是已經失效的設定,例如 OCSP 裝訂——它在多數憑證上已經不再有作用。

二、改完憑證一定要 reload,並確認生效

nginx -s reload 或對應的服務指令。重點是之後要實際連一次確認——reload 可能因為設定錯誤而失敗,而舊的程序會繼續正常服務,從外部看不出任何異狀。

三、明確指定 default_server 與涵蓋範圍

列出所有會連到這台機器的網域,確認每一個都有對應的 server_name,並指定一個專門的 default_server 接住其餘的。沒被涵蓋的網域不會消失,只會靜靜拿到別人的憑證。

排查時該先確認的三件事

遇到憑證相關的回報時,這三項能快速縮小範圍:

  1. 確認是不是 reload 的問題 比對線上憑證的 notBefore 與檔案裡的是否一致。不一致就是還沒載入,這佔了「憑證更新了卻沒生效」的絕大多數。
  2. 確認使用者連的網域有沒有對應區塊 如果錯誤訊息說憑證屬於另一個網域,先查 server_name 是否涵蓋,而不是懷疑憑證本身。那張「別人的憑證」通常就是 default server 的。
  3. 確認憑證檔的排列順序 如果 Nginx 直接以 key values mismatch 啟動失敗,在懷疑私鑰之前先看順序——伺服器憑證必須在最前面。這個錯誤訊息很容易讓人往錯誤的方向查。

小結

nginx -t 通過是必要條件,不是充分條件。它驗證設定能否載入,但憑證在記憶體裡、TLS 交握早於網域判斷、檔案順序決定哪張是伺服器憑證這三件事,都發生在它的檢查範圍之外。

這些機制本身沒有問題,麻煩的是它們讓「設定檔正確」產生了虛假的安全感。如果不想每次部署都逐項確認,我們的 SSL 憑證自動化平台在簽發後直接把完整憑證鏈推播到各節點,減少手動編輯設定檔的環節。

參考資料