2026年10月4日

2026年10月4日

nginxとgunicornでDjangoを本番デプロイする手順(systemd・HTTPS対応)

Django を本番環境で公開するときは、開発用の runserver ではなく、アプリケーションサーバーの gunicorn と、その前段に置く nginx を組み合わせる構成が定番です。この記事では、Ubuntu サーバー上で Django の本番設定を整え、gunicorn を systemd で常駐させ、nginx から静的ファイル配信とリバースプロキシを行い、HTTPS 化するまでを順に説明します。502 エラーが出たときの確認順も最後にまとめます。

構成の全体像

リクエストは次の順に流れます。

  1. ブラウザ → nginx(80/443番ポートで受け付け、HTTPS終端・静的ファイル配信)
  2. nginx → gunicorn(UNIXソケット経由で動的なリクエストだけを転送)
  3. gunicorn → Django(WSGIアプリケーションとして処理)

gunicorn は Python のリクエスト処理に専念し、遅いクライアントへの応答やファイル配信は nginx に任せます。gunicorn を直接インターネットに公開しないのは、この役割分担のためです。

この記事では、プロジェクト名を myproject、配置先を /srv/myproject、実行ユーザーを django、ドメインを example.com とします。Django と gunicorn のバージョンは、サポート期間中のものを使ってください。Django はLTS版とそれ以外でサポート期間が異なるため、公式サイトのサポート表で確認できます(2026年10月現在)。

実行ユーザーと仮想環境を用意する

アプリ専用のユーザーを作り、root では動かしません。

sudo apt update
sudo apt install python3-venv python3-dev nginx
sudo useradd --system --create-home --shell /bin/bash django
sudo mkdir -p /srv/myproject
sudo chown django:www-data /srv/myproject

sudo -u django -H bash
cd /srv/myproject
git clone https://example.com/your/repo.git .
python3 -m venv venv
./venv/bin/pip install -r requirements.txt gunicorn

データベースに PostgreSQL や MySQL を使う場合は、対応するドライバーも requirements.txt に含めます。

Django の本番設定

settings.py は、開発用の設定のまま公開しないことが最も大切です。秘密情報は環境変数から読み込みます。

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = False
ALLOWED_HOSTS = ["example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com"]

STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"

# nginx が X-Forwarded-Proto を上書きして渡す前提の設定
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

各設定の意味は次のとおりです。

  • DEBUG = False にしないと、エラー時に設定値やスタックトレースが外部に表示されます。
  • ALLOWED_HOSTS に公開するドメインを入れないと、DEBUG = False の状態ではすべてのリクエストが 400 エラーになります。
  • CSRF_TRUSTED_ORIGINS は、Django 4.0 以降ではスキーム(https://)付きで書きます。
  • SECURE_PROXY_SSL_HEADER は、nginx が X-Forwarded-Proto ヘッダーを毎回上書きしている場合にだけ安全です。クライアントが送ってきた値をそのまま通す構成では、HTTPS 判定を偽装されるおそれがあります。

環境変数は、権限を絞ったファイルに置きます。

sudo mkdir -p /etc/myproject
sudo tee /etc/myproject/env > /dev/null <<'EOF'
DJANGO_SECRET_KEY=your-secret-key
DJANGO_SETTINGS_MODULE=myproject.settings
EOF
sudo chown root:django /etc/myproject/env
sudo chmod 640 /etc/myproject/env

このファイルはリポジトリに含めません。設定が済んだら、静的ファイルの収集とデプロイ向けのチェックを行います。

sudo -u django -H bash -c 'set -a; . /etc/myproject/env; set +a; cd /srv/myproject && ./venv/bin/python manage.py migrate && ./venv/bin/python manage.py collectstatic --noinput && ./venv/bin/python manage.py check --deploy'

check --deploy は、HSTS やクッキーの設定など、本番で見直すべき項目を警告として表示します。警告の内容を読み、自分の環境に必要なものを設定してください。HSTS は一度有効にすると取り消しに時間がかかるため、短い期間から試すのが安全です。

gunicorn を systemd で常駐させる

ソケットとサービスの2つのユニットファイルを作ります。ソケットを systemd に管理させると、nginx からの接続を受けたときに gunicorn が起動していなければ自動で起動します。

/etc/systemd/system/gunicorn.socket:

[Unit]
Description=gunicorn socket for myproject

[Socket]
ListenStream=/run/gunicorn.sock
SocketUser=www-data
SocketMode=600

[Install]
WantedBy=sockets.target

/etc/systemd/system/gunicorn.service:

[Unit]
Description=gunicorn daemon for myproject
Requires=gunicorn.socket
After=network.target

[Service]
User=django
Group=www-data
WorkingDirectory=/srv/myproject
EnvironmentFile=/etc/myproject/env
ExecStart=/srv/myproject/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    --access-logfile - \
    myproject.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target

ワーカー数は、gunicorn のドキュメントで「(2 × CPUコア数) + 1」が出発点として示されています。メモリ使用量とレスポンスを見ながら調整します。設定したら起動して状態を確認します。

sudo systemctl daemon-reload
sudo systemctl enable --now gunicorn.socket
sudo curl --unix-socket /run/gunicorn.sock -H "Host: example.com" -I http://localhost/
sudo systemctl status gunicorn

curl でソケットに接続したタイミングで gunicorn が起動し、Django の応答ヘッダーが返れば成功です。ソケットは www-data だけが読み書きできる権限なので sudo を付け、ALLOWED_HOSTS に一致させるため Host ヘッダーを指定しています。コードを更新したときは sudo systemctl reload gunicorn でワーカーを入れ替えます。

nginx の設定

/etc/nginx/sites-available/myproject を作ります。

server {
    listen 80;
    server_name example.com;

    client_max_body_size 10m;

    location /static/ {
        alias /srv/myproject/staticfiles/;
    }

    location /media/ {
        alias /srv/myproject/media/;
    }

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

alias のパスは末尾のスラッシュまで location と対応させます。ここがずれると静的ファイルが 404 になります。client_max_body_size はアップロードを受け付ける上限で、既定値のままだと大きなファイルのアップロードが 413 エラーになります。

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

nginx -t で構文エラーがないことを確認してから再読み込みします。

HTTPS 化する

Let's Encrypt の証明書は certbot で取得できます。

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
sudo certbot renew --dry-run

certbot が nginx の設定に 443 番の server ブロックとリダイレクトを追加します。--dry-run で自動更新が動くことも確認しておきます。

502 Bad Gateway が出たときの確認順

nginx が gunicorn に接続できないと 502 になります。よくある原因の順に確認します。

  1. gunicorn が起動していない: sudo journalctl -u gunicorn -n 50 を見ます。ModuleNotFoundError なら仮想環境のパスか WorkingDirectory、KeyError: 'DJANGO_SECRET_KEY' なら環境変数ファイルの読み込みを確認します。
  2. ソケットに接続する権限がない: /var/log/nginx/error.log に Permission denied が出ていれば、ソケットの所有者(SocketUser)と nginx の実行ユーザーが一致しているかを確認します。
  3. ソケットのパスが一致していない: systemd の ListenStream、gunicorn の --bind、nginx の proxy_pass の3か所が同じパスかを見ます。
  4. 処理がタイムアウトしている: 重い処理で gunicorn のワーカーが既定のタイムアウトを超えて再起動されている場合は、ログに WORKER TIMEOUT が出ます。処理を見直すか、非同期のジョブに分けます。

400 エラーの場合は ALLOWED_HOSTS、CSRF 検証エラーの場合は CSRF_TRUSTED_ORIGINS と nginx のヘッダー設定を確認します。

まとめ

  • nginx は HTTPS と静的ファイル、gunicorn は Django の処理と役割を分ける
  • DEBUG = False、ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS を設定し、秘密情報は環境変数で渡す
  • gunicorn は systemd のソケットとサービスで常駐させ、専用ユーザーで動かす
  • nginx の設定は nginx -t で確認してから反映し、certbot で HTTPS 化する
  • 502 はログを見て、起動・権限・ソケットのパス・タイムアウトの順に確認する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲