跳到內容

如何配置 Symfony 以在負載平衡器或反向代理後方運作

編輯此頁面

當您部署應用程式時,您可能會在負載平衡器(例如 AWS Elastic Load Balancing)或反向代理(例如用於快取的 Varnish)後方。

在大多數情況下,這不會對 Symfony 造成任何問題。但是,當請求通過代理時,某些請求資訊會使用標準 Forwarded 標頭或 X-Forwarded-* 標頭發送。例如,使用者真正的 IP 位址將儲存在標準 Forwarded: for="..." 標頭或 X-Forwarded-For 標頭中,而不是讀取 REMOTE_ADDR 標頭(現在將是您的反向代理的 IP 位址)。

如果您未將 Symfony 配置為尋找這些標頭,您將獲得關於用戶端 IP 位址、用戶端是否透過 HTTPS 連線、用戶端埠口以及所請求的主機名稱的不正確資訊。

解決方案:setTrustedProxies()

為了修正這個問題,您需要告訴 Symfony 哪些反向代理 IP 位址是可信任的,以及您的反向代理使用哪些標頭來發送資訊。

您可以在您的機器上設定 SYMFONY_TRUSTED_PROXIESSYMFONY_TRUSTED_HEADERS 環境變數來做到這一點。或者,您可以使用以下配置選項來配置它們

1
2
3
4
5
6
7
8
9
10
11
# config/packages/framework.yaml
framework:
    # ...
    # the IP address (or range) of your proxy
    trusted_proxies: '192.0.0.1,10.0.0.0/8'
    # shortcut for private IP address ranges of your proxy
    trusted_proxies: 'private_ranges'
    # trust *all* "X-Forwarded-*" headers
    trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix']
    # or, if your proxy instead uses the "Forwarded" header
    trusted_headers: ['forwarded']

7.1

Symfony 7.1 中引入了 private_ranges 作為 trusted_proxies 選項的私有 IP 位址範圍的快捷方式。

7.2

Symfony 7.2 中引入了對 SYMFONY_TRUSTED_PROXIESSYMFONY_TRUSTED_HEADERS 環境變數的支援。

危險

啟用 Request::HEADER_X_FORWARDED_HOST 選項會使應用程式暴露於 HTTP Host 標頭攻擊。請確保代理伺服器確實發送了 x-forwarded-host 標頭。

Request 物件有幾個 Request::HEADER_* 常數,用於精確控制信任哪些來自反向代理的標頭。引數是一個位元欄位,因此您也可以傳遞您自己的值(例如 0b00110)。

提示

您可以設定 TRUSTED_PROXIES 環境變數,以便在每個環境中配置代理伺服器

1
2
# .env
TRUSTED_PROXIES=127.0.0.1,10.0.0.0/8
1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    trusted_proxies: '%env(TRUSTED_PROXIES)%'

危險

當使用 nginx realip 模組時,「信任的代理伺服器」功能無法如預期運作。在服務 Symfony 應用程式時停用該模組。

但如果我的反向代理 IP 位址 постоянно 變更怎麼辦!

某些反向代理(例如 AWS Elastic Load Balancing)沒有靜態 IP 位址,甚至沒有您可以使用 CIDR 標記法鎖定的範圍。在這種情況下,您需要 - 非常小心地 - 信任所有代理伺服器。

  1. 配置您的 Web 伺服器,使其回應來自任何用戶端(除了您的負載平衡器)的流量。對於 AWS,可以使用安全群組來完成。
  2. 一旦您保證流量只會來自您信任的反向代理,請將 Symfony 配置為始終信任傳入的請求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # config/packages/framework.yaml
    framework:
        # ...
        # trust *all* requests (the 'REMOTE_ADDR' string is replaced at
        # runtime by $_SERVER['REMOTE_ADDR'])
        trusted_proxies: '127.0.0.1,REMOTE_ADDR'
    
        # you can also use the 'PRIVATE_SUBNETS' string, which is replaced at
        # runtime by the IpUtils::PRIVATE_SUBNETS constant
        # trusted_proxies: '127.0.0.1,PRIVATE_SUBNETS'

7.2

Symfony 7.2 中引入了對 'PRIVATE_SUBNETS' 字串的支援。

就是這樣!至關重要的是,您要阻止來自所有非信任來源的流量。如果您允許外部流量,他們可能會「欺騙」他們真實的 IP 位址和其他資訊。

如果您也在負載平衡器之上使用反向代理(例如 CloudFront),則呼叫 $request->server->get('REMOTE_ADDR') 將不足夠,因為它只會信任直接位於您的應用程式上方的節點(在本例中為您的負載平衡器)。您還需要將任何其他代理伺服器(例如 CloudFront IP 範圍)的 IP 位址或範圍附加到信任的代理伺服器陣列中。

子路徑 / 子資料夾中的反向代理

如果您的 Symfony 應用程式在反向代理後方執行,並且在子路徑/子資料夾中提供服務,則 Symfony 可能會產生不正確的 URL,而忽略反向代理的子路徑/子資料夾。

為了修正這個問題,您需要通過設定 X-Forwarded-Prefix 標頭,將反向代理的子路徑/子資料夾路由前綴傳遞給 Symfony。標頭通常可以在您的反向代理配置中配置。配置 X-Forwarded-Prefix 作為信任的標頭,以便能夠使用此功能。

X-Forwarded-Prefix 由 Symfony 用於為請求物件的基本 URL 添加前綴,該基本 URL 用於在 Symfony 應用程式中產生絕對路徑和 URL。如果沒有標頭,基本 URL 將僅根據執行 Symfony 的 Web 伺服器的配置來確定,這會導致在應用程式由反向代理在子路徑/子資料夾下提供服務時,路徑/URL 不正確。

例如,如果您的 Symfony 應用程式直接在類似 https://symfony.tld/ 的 URL 下提供服務,並且您想使用反向代理在 https://public.tld/app/ 下提供應用程式,您需要在您的反向代理配置中將 X-Forwarded-Prefix 標頭設定為 /app/。如果沒有標頭,Symfony 將根據其伺服器基本 URL(例如 /my/route)而不是正確的 /app/my/route 生成 URL,這是透過反向代理存取路由所必需的。

標頭對於每個反向代理可以是不同的,以便可以正確處理透過在不同子路徑/子資料夾下提供的不同反向代理的存取。

使用反向代理時的自訂標頭

某些反向代理(例如具有 CloudFront-Forwarded-ProtoCloudFront)可能會強制您使用自訂標頭。例如,您有 Custom-Forwarded-Proto 而不是 X-Forwarded-Proto

在這種情況下,您需要在您的應用程式中儘早設定標頭 X-Forwarded-Proto,其值為 Custom-Forwarded-Proto,即在處理請求之前

1
2
3
4
5
6
// public/index.php

// ...
$_SERVER['HTTP_X_FORWARDED_PROTO'] = $_SERVER['HTTP_CUSTOM_FORWARDED_PROTO'];
// ...
$response = $kernel->handle($request);

覆寫隱藏 SSL 終止後方的配置

某些雲端設定(例如在 Microsoft Azure 中使用「Web App for Containers」執行 Docker 容器)執行 SSL 終止並透過 HTTP 連接您的 Web 伺服器,但不更改遠端位址也不設定 X-Forwarded-* 標頭。這表示 Symfony 的信任代理伺服器功能無法幫助您。

一旦您確保您的伺服器只能透過 HTTPS 上的雲端代理伺服器訪問,而不能透過 HTTP 訪問,您可以覆寫您的 Web 伺服器發送到 PHP 的資訊。對於 Nginx,這看起來會像這樣

1
2
3
4
5
6
7
location ~ ^/index\.php$ {
    fastcgi_pass 127.0.0.1:9000;
    include fastcgi.conf;
    # Lie to Symfony about the protocol and port so that it generates the correct HTTPS URLs
    fastcgi_param SERVER_PORT "443";
    fastcgi_param HTTPS "on";
}
這項工作,包括程式碼範例,根據 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本