※この投稿はSymfony3.4.20で調べた内容を元に書いています。
本番リリースしたとたん絶対URLがhttpsにならない(焦
ロードバランサー(SSLオフロード機能有効)配下にSymfonyのアプリケーションを配置してアプリケーション内でURLを生成したところ、URLがhttpで生成されてしまいました。
class IndexController extends Controller
{
public function indexAction(Request $request)
{
$absoluteUrl = $this->generateUrl('ROUTE_NAME', [], UrlGeneratorInterface::ABSOLUTE_URL);
// http://... になってしまった
}
}
対応策は、web/app.phpで
$request = Request::createFromGlobals();
Request::setTrustedProxies(
['CIDR表記'],
Request::HEADER_X_FORWARDED_ALL
);
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
をセットすることです。
ロードバランサー配下では、クライアントがロードバランサーへの接続に使用したプロトコルは$_SERVER['HTTPS']ではなく、X-Forwarded-Protoヘッダに記録されます。
そう、SymfonyはRequest::setTrustedProxies()を設定しさえすれば、HTTPS接続かを見る箇所を切り替えてくれるのです。なんと親切なんでしょう。
Symfonyのソースコードを眺めてみます。
Symfony\Component\HttpFoundation\Requestクラスに
/**
* Gets the request's scheme.
*
* @return string
*/
public function getScheme()
{
return $this->isSecure() ? 'https' : 'http';
}
isSecureの中身は
public function isSecure()
{
if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
}
$https = $this->server->get('HTTPS');
return !empty($https) && 'off' !== strtolower($https);
}
Request::setTrustedProxies()の設定にひっかかれば、前半のif文の中の処理で動作しています。
IPアドレスでも同じことが…
ロードバランサー配下では、クライアントがロードバランサーへの接続に使用したIPアドレスは$_SERVER['REMOTE_ADDR']ではなく、X-Forwarded-Forヘッダに記録されます。
Request::geClientIp()もきちんと対応してくれています。
public function getClientIp()
{
$ipAddresses = $this->getClientIps();
return $ipAddresses[0];
}
public function getClientIps()
{
$ip = $this->server->get('REMOTE_ADDR');
if (!$this->isFromTrustedProxy()) {
return array($ip);
}
return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
}
getClientIp()は、getClientIps()で配列で返ってきたIPの先頭を返しています。
ということは、getClientIps()がIPアドレスを複数返すことがあるんです。
これはどういうケースかというと WAF → LB → アプリケーション のようにプロキシを2段かましたときに、X-Forwarded-ForにIP1, IP2
のように記録されるからです。
SymfonyはこんなIPにもきっちり対応して、カンマでIPを分解して処理してくれています。