TLDR

  • 問題:將 Nginx 配置從測試環境遷移到正式環境後,無法接收 SSE
  • 原因:Nginx 1.22.0 預設使用了 HTTP/1.0 而不是 HTTP/1.1
  • 調查方法:啟用調試日誌並分析
  • 解決方案:在 Nginx 全局設置中指定 HTTP 版本為 1.1

情境

使用一樣的 Config,從測試環境搬移到正式環境後,一般請求皆正常,但 SSE 無回應(Timeout)。

經過人工、工具比對,以及 ChatGPT 分析,都沒有發現設定上的問題。 唯一差異在於測試環境是 1.22.1,而正式環境的 Nginx 版本是 1.22.0。

調查過程

啟用調試日誌

首先在 /etc/nginx/nginx.conf 中啟用調試日誌,印出詳細的錯誤:

1
error_log /var/log/nginx/error.log debug;

檢查配置並重新加載 Nginx:

1
2
nginx -t
nginx -s reload

分析日誌

我們可以在 /var/log/nginx/error.log 找到錯誤資訊。

若是緊急線上修復,且很多使用者在線,可能導致 log 過多難以查找,這時候可以用以下方法:

1
tail -f -n 1 /var/log/nginx/error.log | grep -i "sse" # sse可替換為你想要查找的關鍵字

這行指令會持續追蹤 error.log 的最新一行,並且過濾出含有 “sse” 的 log。

接著去操作出問題的 API,得到以下資訊:

1
2
3
4
5
6
# 已替換敏感資訊並省略字數
[notice] XXXX#XXXX: _XXXX "^/app/(._)$" matches "/app/api/sse", client: X.X.X.X, server: example.com, request: "GET /app/api/sse HTTP/1.1", host: "example.com", referrer: "https://example.com/app/Game"
[notice] XXXX#XXXX: *XXXX "^/app/(.*)$" matches "/app/api/sse", client: X.X.X.X, server: example.com, request: "GET /app/api/sse HTTP/1.1", host: "example.com", referrer: "https://example.com/app/Game"
[notice] XXXX#XXXX: *XXXX rewritten data: "/root/api/sse", args: "", client: X.X.X.X, server: example.com, request: "GET /app/api/sse HTTP/1.1", host: "example.com", referrer: "https://example.com/app/Game"
[error] XXXX#XXXX: *XXXX upstream timed out (110: Connection timed out) while reading upstream, client: X.X.X.X, server: example.com, request: "GET /app/api/sse HTTP/1.1", upstream: "http://127.0.0.1:80/root/api/sse", host: "example.com", referrer: "https://example.com/app/Game"
[info] XXXX#XXXX: \*XXXX epoll_wait() reported that client prematurely closed connection, so upstream connection is closed too while reading upstream, client: 127.0.0.1, server: example.com, request: "GET /root/api/sse HTTP/1.0", upstream: "http://X.X.X.X:8888/sse", host: "127.0.0.1", referrer: "https://example.com/app/Game"

發現問題

仔細看可以發現在最後一行,Nginx 沒有正確吃到 HTTP/1.1 的設定並錯誤的使用了 HTTP/1.0,才導致 SSE 傳送失敗。

即使在 Config 中所有地方都設定 proxy_http_version 1.1;,也無法順利解決。

推測因不明原因(可能是版本差異),Nginx 忽略設定檔,使用預設的 HTTP/1.0 來呼叫 SSE。

解決方案

直接在 http 區塊中,將全域 proxy_http_version 預設改為 1.1。 強制 Nginx 在所有連接中使用 HTTP/1.1,包括與 upstream 的連接等。

1
2
3
4
# 不同專案可能會有不同設定
http {
    proxy_http_version 1.1;
}

結論

  1. 版本差異可能導致意想不到的問題,即使配置文件相同。
  2. 啟用詳細日誌對於診斷問題非常有幫助。
  3. 有時全局設置可以解決特定問題,特別是當單個指令似乎不起作用時。
  4. 在進行環境遷移時,務必仔細檢查所有相關軟件的版本。