はじめに
個人開発でDjagoを用いたWebアプリケーションをAWS EC2環境へNginx + Gunicorn構成でデプロイした際、ローカル環境では正常に表示されていた投稿画像(メディアファイル)が、本番環境ですべて'404 Not Found'となり表示されなくなるトラブルが起きました。
調査の結果、「DEBUG=False時のDjangoの仕様」 と 「Nginxの設定パスとDjangoのディレクトリ構造の不一致」 が原因であると判明しました。
本記事では、このエラーの原因特定から解決までのプロセス及びインフラ構成における役割分担についてまとめました。
開発環境・構成
- OS: Ubuntu (AWS EC2)
- 言語・フレームワーク: Python 3.6 / Django
- WSGIサーバー: Gunicorn
- Webサーバー: Nginx
- プロトコル: HTTPS (Let's Encrypt / Certbot)
1. 遭遇したトラブル
ローカル開発時('python manage.py runserver')は管理画面からアップロードした記事画像が問題なく表示されていましたが、AWS EC2へデプロイして本番公開('DEBUG=False')に切り替えた後、ブラウザで記事一覧を確認すると画像がすべて読み込めなくなりました。
ブラウザのデベロッパーツールのコンソールを確認したところ、以下のようなエラーが記録されていました。
Failed to load resource: the server responded with a status of 404 (Not Found)
2. 原因の仮説検証
① DEBUG=FalseによるDjangoの仕様変更
開発環境(DEBUG=True)では、Django自身が開発サーバーとしてメディア画像などの静的リソースを配信してくれます。
しかし、本番環境(DEBUG=False)に切り替えると、セキュリティやパフォーマンスの観点からDjangoは画像ファイルを自前で配信しなくなる仕様になっています。
そのため、Nginx(最前段のWebサーバー)側で画像要求が来たらディスク上の保存先から直接配信する設定を行う必要があります。
②Nginx設定と実際の保存先パスのずれ
Nginx設定を確認しました。
当初は以下のコードのみ記述していました。
location /static/ {
root /home/ubuntu/myblogapp;
}
Nginx側に「/pics/に来たらサーバー内の画像保存フォルダを直接返す」という設定が入っていないと画像が表示されないとわかり、Nginxの設定に以下の記述を追加しました。
location /pics/ {
alias /home/ubuntu/myblogapp/pics/;
}
しかし、ターミナルで実態ファイルを確認したところエラーが発生しました。
$ ls -la ~/myblogapp/pics/
ls: cannot access '/home/ubuntu/myblogapp/pics/': No such file or directory
サーバー上に /pics/ というディレクトリ自体が存在していませんでした。
そこで、models.py と settings.py の定義を再確認しました。
# posts/models.py
image = models.ImageField(upload_to='media/')
# myblogapp/settings.py
MEDIA_URL = '/pics/'
MEDIA_ROOT = BASE_DIR # /home/ubuntu/myblogapp 直下
この組み合わせだと、Djangoは画像ファイルを MEDIA_ROOT + upload_to = /home/ubuntu/myblogapp/media/ に保存し、ブラウザに対しては MEDIA_URL + upload_to + ファイル名 = /pics/media/画像名 というURLを生成します。
つまり、実際の画像はmedia/に保存されているにもかかわらず、Nginxには存在しないpics/ディレクトリを見に行くよう指示していたため404エラーが発生していたことが判明しました。
3. 解決策
まず、サーバー上にメディア保存用ディレクトリが存在し、Webサーバーからアクセス可能な権限になっているかどうか確認します。
ls -ld ~/myblogapp/media
ディレクトリが存在しない場合は作成します(mkdir -p ~/myblogapp/media)。
既に存在している場合も、Nginxから読み取り・実行ができるようパーミッションを755に設定します。
chmod -R 755 ~/myblogapp/media
Nginxの設定ファイルを開き、/pics/および/pics/media/へのアクセスが実際の保存先である/home/ubuntu/myblogapp/media/を参照するようにaliasを修正しました。
sudo nano /etc/nginx/sites-available/myblogapp
location /static/ {
alias /home/ubuntu/myblogapp/static_collected/;
}
location /pics/media/ {
alias /home/ubuntu/myblogapp/media/;
}
locaiton /pics/ {
alias /home/ubuntu/myblogapp/media/;
}
修正後、Nginxの構文エラーがないかチェックした上で、設定を反映しました。
sudo nginx -t
sudo systemctl reload nginx
ブラウザで再読み込みをしたところ、404エラーが解消され、投稿画像が正常に画面に表示されるようになりました。
4. まとめ
最初、ホーム画像を.pngから.jpegに変更したことがエラーの原因だと思い込んで迷走していましたが、実はブラウザのキャッシュで見えていただけだと判明し、DEBUG=False時のNginxとDjangoの役割分担に気づきました。
Django(Python)のようなアプリケーションサーバーに静的メディアファイルの配信を任せると、負荷が高くなりDB操作やHTML生成が遅延します。インターネット最前線に位置するNginxがディスクから直接ファイルを返す設計にすることで、サーバー全体の高速化と負荷軽減につながることを実感しました。
MEDIA_URLとMEDIA_ROOTがどのように紐付いているのかを正確に把握していなければ、本番環境で思わぬパス不一致エラーを引き起こすことを学びました。
このエラーの解決のためには、「ブラウザがどのURLを要求しているか」、「実ファイルはディスクのどこにあるか」、「Webサーバーはどうルーティングしているか」を1つずつ順を追って切り分けることが必要です。