Nginx代理websocket為什麼要這樣做?

發表於2022-05-10

Nginx反向代理websocket

示例:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        ...

        location /chat/ {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }

也許你也曾遇到為什麼配置ws/wss協議相關的代理時總是不順利?最後一番搜尋發現需要加上面三行,於是二話不講,ctrl+c/ctrl+v 一套帶走,reload一下, 完成了。

那麼這三行到底有什麼特殊本領呢?簡單看看:

proxy_http_version 1.1;

這一行沒啥說的,設定http協議版本1.1, 這個主要是為了下面的兩行做準備。

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

這兩行就是設定兩個請求頭 UpgradeConnection,這兩個請求頭都是逐跳標頭(只能傳輸一次,不能透傳), 後端ws程式根據這兩個頭攜帶的資訊來判斷是否使用ws協議來通訊。

  • Upgrade
    能且只能在http1.1版本中使用, 用來標識協議升級/轉換, 在我們這篇文章的背景下,這個頭資訊一般是: Upgrade: websocket; 表示客戶端希望使用websocket協議通訊, 那麼後端的ws程式取到頭資訊後會返回101狀態碼(協議轉換),此時瀏覽器就會使用當前的TCP連線建立websocket通道。

  • Connection
    在本篇文章的背景下, Connection頭資訊取值upgrade, 表示本次請求是一次協議升級(協議轉換)請求, 配合 Upgrade: websocket資訊, 完整表達了這個請求要升級到websocket協議。

為什麼要顯示指定升級頭?

上面提到了反向代理和逐跳標頭,客戶端發起請求時是和反響代理伺服器建立請求, 此時客戶端攜帶的 Upgrade、Connection頭是不會被反向代理伺服器直接轉發到後端服務的(這就是逐跳標頭), 後端服務獲取不到這兩個頭資訊自然也不會主動去切換協議。

因此,需要在反向代理伺服器轉發上游時帶上客戶端原來的請求頭,才可以完成協議的升級或切換。

容易遇到的問題

  1. 需要注意多層反向代理的場景,都要顯示指定頭資訊才行,否則不得行。
  2. wss只要在最外層的代理伺服器上配置即可, 內層的代理伺服器使用ws協議互動。

相關文章