概要
ECS
において、サイドカーコンテナ構成を使用してRails
とNginx
を組み合わせてシステムを構築しました。その際、Rails
のリダイレクト処理においてActionController::Redirecting::UnsafeRedirectError
と言うエラーが発生し、動作が停止する問題に直面しました。このエラーを解決するまでに多くの時間を要した為、本記事ではエラーの原因ならびに解決方法を詳細に解説します。
エラー内容
"ActionController::Redirecting::UnsafeRedirectError",
"Unsafe redirect to \"http:///〇〇\", pass allow_other_host: true to redirect anyway."
]
},
"duration": 380000,
"view": 0,
"exception_backtrace": [
# 省略
],
"service": "test",
"action": "index",
"http": {
"url_details": {
"path": "/"
},
"status_code": 500,
"method": "GET"
},
"db": 0,
"timestamp": 1732793088746
}
}
}
ActionController::Redirecting::UnsafeRedirectError: Unsafe redirect to "http:///login", pass allow_other_host: true to redirect anyway.
/usr/local/bundle/gems/actionpack-7.0.4.3/lib/action_controller/metal/redirecting.rb:193:in `_enforce_open_redirect_protection': Unsafe redirect to "http:///login", pass allow_other_host: true to redirect anyway. (ActionController::Redirecting::UnsafeRedirectError)
以下省略
原因
-
Application Load Balancer(ALB)
→Nginx
→Rails
間の通信で、リダイレクト先のURLを生成する為に必要なホストとスキーム情報が正しく伝達されなかった為に発生 -
Rails
の設定にconfig.force_ssl
をture
する事で、Rails
が全ての通信をHTTPS
に統一し、リダイレクト先のURL
を正しく作成出来る様になり、エラーが解消される
補足
ホスト(Host)
-
Web
のリクエストがどのサーバー(ドメイン名やIPアドレス)に向かっているかを示す情報 - リクエストヘッダーの
Host
フィールドで指定する
Host: example.com
スキーム(Scheme)
- リクエストがどの通信プロトコル(
http
またはhttps
)を使っているかを示す情報 -
Nginx
やALB
で設定されるX-Forwarded-Proto
ヘッダーに含まれている
X-Forwarded-Proto: https
解決方法
-
Nginx
とRails
に対して下記の設定を行う
Nginxの修正
-
Nginx
のdefault.conf
または任意のconf
ファイル内に修正を行う - 修正箇所は、リバースプロシキの設定内に行う
server {
# 省略
# リバースプロキシの設定
location / {
proxy_pass http://web;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
# 省略
}
リバースプロキシ内で使用しているメソッドと説明
メソッド | 説明 |
---|---|
proxy_pass | リバースプロキシとしての転送先を指定する。指定したURL (例: http://web )にリクエストを転送する。Unix ソケットやIP アドレスも指定が可能。URL の末尾にスラッシュを付けるかどうかで転送先パスの挙動が変わる為、設定に注意が必要 |
proxy_set_header X-Real-IP | クライアントの実際のIPアドレスをバックエンドに送信する。通常、バックエンド側でクライアントのIPアドレスを取得する為に必要 |
proxy_set_header X-Forwarded-Proto | クライアントが使用しているプロトコル(http またはhttps )をバックエンドに通知する。この情報により、Rails 等が正しいスキームを認識できる |
proxy_set_header X-Forwarded-For | クライアントIP アドレスのチェーンをバックエンドに送信する。プロキシを通過した全てのIP アドレス情報を保持し、クライアントの追跡を可能にする。$proxy_add_x_forwarded_for がデフォルト設定。独自のカスタム値も指定が可能 |
proxy_set_header Host | 元のリクエストに含まれるHost ヘッダーをバックエンドにそのまま送信する。バックエンドでリクエストのホスト情報を正しく認識する為に必要。$host を使用すると、Nginx の解決したホスト名を送信可能。$http_host は元のリクエストをそのまま使用する |
proxy_redirect | バックエンドからのリダイレクトレスポンスをクライアント向けに書き換える。off を指定すると、リダイレクトレスポンスはそのままクライアントに送信される。リダイレクトURL をカスタマイズする場合、正規表現を使って書き換え規則を設定可能(例: proxy_redirect /old /new ) |
Railsの修正
-
config/environments/production.rb
を下記の設定にする - クラウドのサーバで動作する
staging
や検証、テスト等の環境がある場合も、ECSで尚且つサイドカーコンテナー仕様のRails
、Nginx
で動作している場合は、必要に応じて下記の記述を追加する
公式の説明
3.2.35 config.force_ssl
すべてのリクエストをHTTPSプロトコル下で実行するよう強制し、URL
生成でも"https://"
をデフォルトのプロトコルに設定します。HTTPSの強制はActionDispatch::SSL
ミドルウェアによって行われ、config.ssl_options
で設定できます。詳しくはAPIドキュメントActionDispatch::SSLを参照してください。
require "active_support/core_ext/integer/time"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# 省略
# コメントアウトを外す
config.force_ssl = true
# 省略
end
注意事項
注意 : ローカル環境では、config.force_ssl
の設定を適用しない
HTTPSが必須になる
-
config.force_ssl = true
を設定すると、すべてのHTTPリクエストがHTTPSにリダイレクトされる - ローカル環境で
SSL
証明書を設定していない場合、HTTPリクエストが失敗する為、動作確認が出来なくなる
HSTSの影響
-
HSTS
が有効な場合、一度でもHTTPS
でアクセスしたドメインはブラウザ側で「HTTPSでしか通信しない」という状態がキャッシュされる - ローカル環境で
HSTS
が設定されると、仮に後でforce_ssl
を無効にしてもブラウザがHTTP
での接続を許さなくなり、リセットが必要になる
ローカル環境にHSTSの設定を行ってしまった場合の対処方法
Chromeの場合
- 手順1:
chrome://net-internals/#hsts
にアクセス - 手順2:
Delete domain security policies
に対象のドメインを入力して削除 - 手順3: ブラウザを再起動
参考資料
参考資料
まとめ
表題のエラーに関しては、通信経路やヘッダーの仕組みについての理解が不足していた為、問題解決に多大な時間を費やしました。この経験を通して、ネットワークやプロトコルに関する知識の重要性を改めて痛感しました。今後は、ネットワークやHTTP
ヘッダー、リバースプロキシ等の基礎から応用までの知識を深め、より効率的にトラブルシューティングが出来る様にする為、本やネットを利用して知識を深めていきたいと思いました。