ActionController::Redirecting::UnsafeRedirectError: Unsafe redirect to "http:///〇〇", pass allow_other_host: true to redirect anyway.

# 概要
`ECS`において、サイドカーコンテナ構成を使用して`Rails`と`Nginx`を組み合わせてシステムを構築しました。その際、`Rails`のリダイレクト処理において`ActionController::Redirecting::UnsafeRedirectError`と言うエラーが発生し、動作が停止する問題に直面しました。このエラーを解決するまでに多くの時間を要した為、本記事ではエラーの原因ならびに解決方法を詳細に解説します。

# エラー内容

```detadog.log
"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
		}
	}
}
```

```rails.log
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`ファイル内に修正を行う
- 修正箇所は、リバースプロシキの設定内に行う

```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](https://api.rubyonrails.org/v8.0/classes/ActionDispatch/SSL.html)を参照してください。

https://railsguides.jp/configuring.html#config-force-ssl

```rb
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  
```

### 注意事項
:::note alert
注意 : ローカル環境では、`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: ブラウザを再起動

参考資料
- [HSTS が原因で、ウェブサイトが勝手にhttps接続しないようにする](https://laboradian.com/disable-hsts-for-domain/)
:::



# 参考資料
- [[AWS Copilot CLI] サイドカー](https://aws.github.io/copilot-cli/ja/docs/developing/sidecars/)
- [[EGセキュアソリューションズ] プロキシとは？フォワードプロキシやリバースプロキシの
仕組みについても解説](https://siteguard.jp-secure.com/blog/reverse-proxy)


# まとめ
表題のエラーに関しては、通信経路やヘッダーの仕組みについての理解が不足していた為、問題解決に多大な時間を費やしました。この経験を通して、ネットワークやプロトコルに関する知識の重要性を改めて痛感しました。今後は、ネットワークや`HTTP`ヘッダー、リバースプロキシ等の基礎から応用までの知識を深め、より効率的にトラブルシューティングが出来る様にする為、本やネットを利用して知識を深めていきたいと思いました。