# Djangoでファイルを「直接」配信せず、Nginxに「代わりに」配信させる:X-Accel-Redirectでダウンロード性能を向上させる 多くのDjangoアプリケーションでは、保護されたファイル(ログインユーザーのみ、購入後に閲覧可能など)を配信する際に、`FileResponse` のように **Pythonプロセスがファイルを読み取り** クライアントへ送信します。トラフィックが小さく、内部サーバ間の通信であれば、この方法でも十分です。 しかし、ファイルリクエストが急増すると、**アプリケーションサーバ(Python)がファイル配信に時間を取られ**、権限チェックやビジネスロジック、API処理が滞ります。そこで「権限チェックはDjangoが行い、ファイル転送はNginxに任せる」代表的な手法が **X-Accel-Redirect** です。 --- ## なぜPythonがファイルを直接送るとボトルネックになるのか {#sec-bc9d8bcff427} Djangoがファイルを直接配信する流れは次のようになります。 1. リクエスト受信 2. 権限チェック 3. ディスク/ストレージからファイル読み込み 4. アプリケーションプロセスがネットワークへ送信(ストリーミング) 問題は **3〜4が「重い作業」** である点です。 - ファイルが大きいほど送信時間が長くなる - 同時ダウンロードが増えるとワーカー/スレッド/プロセスが次第にロックされる - 結局API応答遅延、タイムアウト、サーバ増設の圧迫につながる 対照的に **Nginxは静的ファイル配信に最適化** されていて、カーネルレベル最適化(`sendfile`)、効率的なイベントループ、バッファリング/レンジリクエスト処理など「ファイル配信」に特化した機能を活用します。 --- ## X-Accel-Redirectの核心アイデア {#sec-fb6b7f4d3d0a} **Djangoは「検査のみ」し、Nginxは「転送のみ」する**。 ### 動作原理 {#sec-3274391f7151} 1. クライアントが `/download/123` のようなURLをリクエスト 2. DjangoがDB照会/権限チェックのみ実行 3. Djangoがレスポンスヘッダーに以下のように記載し、**空ボディで返却** ``` X-Accel-Redirect: /_protected/real/path/to/file.webp ``` 4. Nginxがこのヘッダーを見て **内部的にファイルを探し、クライアントへ直接送信** - Djangoはファイル内容を直接読み取らない つまり、Djangoは「このユーザーがこのファイルを取得してもよいか?」だけを担い、実際のファイル転送はNginxに委ねます。 --- ## この手法が特に有効なケース {#sec-fe558f93fab5} 以下の状況で効果が大きくなります。 ### 1) ダウンロード/画像リクエストが多く、同時性が高いサービス {#sec-099cf763a389} - コミュニティ/メッセンジャー画像、添付ファイル、レポートPDFダウンロード - 「リクエスト数は多いがロジックは単純」なパターンではX-Accel-Redirectが光ります。 ### 2) ファイルサイズが大きい、またはRangeリクエストが重要なサービス {#sec-0619dad236e7} - 動画/音声/大容量圧縮ファイル - ブラウザ/プレイヤーが `Range`(区間リクエスト)で再生/再取得する場合、Nginxがこれをより安定して処理します。 ### 3) アプリサーバコストを抑えたいとき {#sec-1fef6421ee9d} - Pythonワーカーは高価(メモリ/CPU)で、ファイル転送に絡むと「お金が漏れる」構造になります。 - ファイル転送をプロキシ層に任せることで、**アプリサーバはロジック処理に集中**できます。 --- ## 逆に、使わなくてもよいケース {#sec-55c54ffd54b0} - 内部サーバ間の通信でトラフィックが低い - ファイルリクエストが少なく、ほとんどがAPI/DBロジックがボトルネック - ファイルがローカルディスクではなくS3など外部オブジェクトストレージで、既にCDN/プリサインURLで解決できる構造 このような場合は `FileResponse` でも運用上十分です。必要になったときに導入しても遅くはありません。 --- ## 実装例:Django + Nginx {#sec-292d314f5987} ![ウェブリクエスト処理フローチャート](/media/editor_temp/6/a1ad0374-979a-447e-b586-81ac02f2b447.png) ### Nginx設定例 {#sec-55b3451a748d} ポイントは `internal` です。 `internal` に設定された location は **クライアントが直接アクセスできず**、**X-Accel-Redirect などの内部リダイレクトからのみ** アクセス可能です。 ```nginx # 実際に保護ファイルを配信する内部エンドポイント location /_protected/ { internal; # 実際のファイルがあるディレクトリ alias /var/app/protected_media/; # 性能オプション(環境に合わせて) sendfile on; tcp_nopush on; # 必要に応じてキャッシュ/ヘッダー制御可能 # add_header Cache-Control "private, max-age=0"; } ``` - `/var/app/protected_media/` 以下に実際のファイルがあると仮定 - 外部に公開されるURLは `/download/...` のような Django ルート - 内部転送パスは `/_protected/...` に統一 --- ### Djangoビュー例 {#sec-ca8548dd9097} Djangoは権限だけ確認し、ファイル内容を読み取らずヘッダーだけを返します。 ```python from django.http import HttpResponse, Http404 from django.contrib.auth.decorators import login_required from django.utils.encoding import iri_to_uri @login_required def download(request, file_id): # 1) DB照会 + 権限チェック obj = get_file_object_or_404(file_id) # 例 if not obj.can_download(request.user): raise Http404 # 2) 内部パス構築(Nginx の /_protected/ 以下にマッピング) internal_path = f"/_protected/{obj.storage_relpath}" # 3) X-Accel-Redirect ヘッダーのみ設定し、ボディは空 response = HttpResponse() response["X-Accel-Redirect"] = iri_to_uri(internal_path) # (任意)ダウンロードファイル名/コンテンツタイプ指定 response["Content-Type"] = obj.content_type or "application/octet-stream" response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"' return response ``` ポイント: - `FileResponse(open(...))` のようなファイルI/Oはありません。 - Djangoはリクエストごとの処理時間が非常に短くなり、ワーカーがファイル転送でロックされません。 --- ## セキュリティチェックリスト {#sec-f111085ed400} ### 1) 内部パスは必ず「サーバが決定」 {#sec-b8b480a71cf8} - クライアント入力で `/_protected/../../etc/passwd` のようなパスが生成されないように - DBに保存された「安全な相対パス」だけを使用するか、ホワイトリストベースでマッピングしてください。 ### 2) Nginx location は必ず `internal` {#sec-7cdefa5663ff} - `internal` が無ければ、ユーザーが `/_protected/...` を直接叩いて回避ダウンロードできてしまいます。 ### 3) 権限チェックロジックは Django のみで信頼 {#sec-4489e3846905} - Nginx は「転送エンジン」役で、アクセス制御は Django が担う構造が安全です。 --- ## 第三者サービスを利用する代替案 {#sec-ff63053f7cda} コストに余裕があれば、最初からファイルを第三者ストレージで提供する設計も可能です。コストはかかりますが、安定性が高く、サーバリソースを節約できます。プロジェクトチームの状況に合わせて選択してください。 - **CDNキャッシュ**:公開ファイルなら Nginx 前に CDN キャッシュがより効果的 - **プリサインURL(S3 等)**:オブジェクトストレージベースなら X-Accel-Redirect の代わりにプリサインURLがよりシンプルな場合もあります --- ## まとめ {#sec-87db4841ff6c} 一般に、ファイル配信はウェブアプリケーション側で行うより、Nginx にオフロードしたほうが性能面で有利です。**静的配信に最適化された Nginx がカーネル最適化まで活用して処理**するためです。 その結果、アプリサーバは「権限チェック + ビジネスロジック」に集中でき、ダウンロードトラフィックが増えても全体システムがはるかに安定します。 トラフィックが大きくない場合は `FileResponse` でも十分です。ただし「ファイルリクエストが急増したときにアプリサーバが崩壊するパターン」は非常に一般的で、その際に最も速く効果を得られるカードが **X-Accel-Redirect** です。 キーワードを一つ覚えておくと良いでしょう: **「権限は Django、転送は Nginx」**。 --- **連関ポスト** - [nginxを使った実践的ロードバランシングガイド](/ja/whitedec/2025/12/11/nginx-load-balancing-guide/) - [リバースプロキシとは?フォワードプロキシとの違い、目的、使用シナリオを一挙に整理](/ja/whitedec/2025/12/10/reverse-proxy-forward-proxy-differences/)