CloudFront → ALB → Django 構成で API レスポンスの URL スキームが http:// になる問題と解決策
はじめに
AWS の CloudFront + ALB + ECS Fargate で Django REST Framework (DRF) の API サーバーを運用していたところ、API レスポンスに含まれる URL が http:// で返されるという問題に遭遇しました。本記事では原因の調査過程と、最終的な解決策を紹介します。
構成
Client (HTTPS)
↓
CloudFront (SSL終端, us-east-1)
↓ HTTP
ALB (HTTP:80のみ受付, ap-northeast-1)
↓ HTTP
ECS Fargate (Gunicorn + Uvicorn, port 9000)
↓
Django REST Framework
CloudFront がSSLを終端し、ALB へは HTTP で転送する構成です。
問題
DRF の API ルート (/api/rest/) にアクセスすると、レスポンスに含まれる URL がすべて http:// になっていました。
| |
モバイルアプリがこの URL を使って後続のリクエストを組み立てるため、HTTPS を前提とした通信が失敗する原因になっていました。
最初の対応: SECURE_PROXY_SSL_HEADER(不十分)
Django には SECURE_PROXY_SSL_HEADER という設定があり、リバースプロキシが付与するヘッダーを見てリクエストが HTTPS かどうかを判定できます。
| |
一般的なリバースプロキシ構成ではこれで解決するはずですが、今回の構成では機能しませんでした。
原因: ALB が X-Forwarded-Proto を上書きする
調査の結果、以下のことがわかりました:
- CloudFront はクライアントが HTTPS でアクセスしたことを知っているので、
X-Forwarded-Proto: httpsをオリジンに転送できる - しかし ALB は受信したプロトコルに基づいて
X-Forwarded-Protoヘッダーを常に上書き する - CloudFront → ALB は HTTP 通信なので、ALB は
X-Forwarded-Proto: httpを設定する - Django は
X-Forwarded-Proto: httpを受け取り、リクエストを HTTP と判定する
つまり、CloudFront が正しい値を送っても、ALB が途中で上書きしてしまうため Django まで届かないのです。
CloudFront: X-Forwarded-Proto: https ← クライアントはHTTPS
↓
ALB: X-Forwarded-Proto: http ← ALBが受信プロトコル(HTTP)で上書き
↓
Django: "リクエストはHTTPだ" → http:// でURL生成
解決策: CloudFront カスタムオリジンヘッダー
ALB は X-Forwarded-Proto 等の標準ヘッダーを上書きしますが、カスタムヘッダーには干渉しません。
CloudFront のオリジン設定でカスタムヘッダーを追加し、Django 側でそのヘッダーを参照するようにしました。
インフラ側 (Terraform)
CloudFront のオリジン設定にカスタムヘッダーを追加:
| |
アプリ側 (Django)
SECURE_PROXY_SSL_HEADER の参照先をカスタムヘッダーに変更:
| |
補足: Django は HTTP ヘッダーを
HTTP_プレフィックス + 大文字 + ハイフンをアンダースコアに変換した形でrequest.METAに格納します。X-Forwarded-Ssl→HTTP_X_FORWARDED_SSLとなります。
結果
| |
API レスポンス中の URL が正しく https:// で返されるようになりました。
ヘッダーの流れ(修正後)
CloudFront:
X-Forwarded-Proto: https ← ALBに上書きされる(使わない)
X-Forwarded-Ssl: on ← カスタムヘッダー(ALBは干渉しない)
↓
ALB:
X-Forwarded-Proto: http ← ALBが上書き
X-Forwarded-Ssl: on ← そのまま通過 ✓
↓
Django:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_SSL', 'on')
→ "リクエストはHTTPSだ" → https:// でURL生成 ✓
対応順序の注意点
ALB のヘルスチェックパスも同時に変更する場合、デプロイ順序に注意が必要です。
NG な順序:
- Terraform apply でヘルスチェックパスを変更
- アプリをデプロイ → 新しいヘルスチェックパスに応答できるコードがまだないため、ヘルスチェック失敗
正しい順序:
- アプリをビルド・デプロイ(新しいヘルスチェックエンドポイントを含む)
- 動作確認
- Terraform apply でヘルスチェックパスを変更
まとめ
| 方法 | 仕組み | CloudFront → ALB 構成で有効か |
|---|---|---|
X-Forwarded-Proto | 標準ヘッダーでプロトコル判定 | ❌ ALB が上書きする |
X-Forwarded-Ssl (カスタムヘッダー) | CloudFront が付与、ALB は干渉しない | ✅ 確実に動作 |
CloudFront → ALB → アプリケーション という構成では、標準の X-Forwarded-Proto は ALB に上書きされる ことを念頭に置く必要があります。カスタムヘッダーを使うことで、ALB を経由しても確実にオリジナルのプロトコル情報をアプリケーションに伝達できます。