SSL終端がALBのとき、Railsのforce_sslがどのように動作するのか疑問に思ったので調べてみました。
こちらの記事を大いに参考にさせていただきました。
force_ssl における動作
ALBにhttpsでリクエストするとALBがX-Forwarded-Proto: https
ヘッダを追加してRailsにリクエストしてくれます。
HTTP ヘッダーと Application Load Balancer - Elastic Load Balancing
そのヘッダを用いてRackの内部ではSSL判定がなされています。
https://github.com/rack/rack/blob/3-0-stable/lib/rack/request.rb#L410
def ssl? scheme == 'https' || scheme == 'wss' end
https://github.com/rack/rack/blob/3-0-stable/lib/rack/request.rb#L249
def scheme if get_header(HTTPS) == 'on' 'https' elsif get_header(HTTP_X_FORWARDED_SSL) == 'on' 'https' elsif forwarded_scheme forwarded_scheme else get_header(RACK_URL_SCHEME) end end
https://github.com/rack/rack/blob/3-0-stable/lib/rack/request.rb#L713
def forwarded_scheme forwarded_priority.each do |type| case type when :forwarded if (forwarded_proto = get_http_forwarded(:proto)) && (scheme = allowed_scheme(forwarded_proto.last)) return scheme end when :x_forwarded x_forwarded_proto_priority.each do |x_type| if header = FORWARDED_SCHEME_HEADERS[x_type] split_header(get_header(header)).reverse_each do |scheme| if allowed_scheme(scheme) return scheme end end end end end end nil end
ヘルスチェックでリダイレクトを回避する
にもあるように、HTTPで通信かつステータス200で成功判定している場合はforce_sslを有効化するとリダイレクトしてしまうので、 ELB-HealthCheckerをUAに含む場合リダイレクトしない設定をすると良いようです。
request specをどう書くべきか
下の記事のように、ActionDispatch::Integration::RequestHelpersのヘルパーメソッドをオーバーライドするといいみたいです。
Request Specでデフォルトのヘッダーを設定する | Ver.1.0
オプションは他にもあるので、下記のようにpathとheader以外は可変長引数にした方がいいです。
module RequestSpecHelper [:get, :post, :patch, :put, :delete, :head].each do |name| define_method(name) do |path, headers: {}, **args| super(path, headers: headers.merge({ "X-Forwarded-Proto": "https" }), **args) end end end
大抵のケースは上記でカバーできる一方で、Specが特定条件下で使用しているCookieJarがSecure Cookieをサポートしていないため、Secure Cookieが絡むテストが一部NGになることがあります。このようなケースを考慮するとテスト環境ではforce_ssl: false にした方が無難そうです。