14.

Django アプリの本番開始 URL(プレフィックス)を変更する方法(Nginx / gunicorn / uWSGI)

編集
この記事の要点
  • Django アプリを https://example.com/myapp/ のようなサブパス配信にする手順
  • urls.pyurlpatternspath('myapp/', include('app.urls')) を加えるか、FORCE_SCRIPT_NAME = '/myapp' を設定
  • リバースプロキシ側で location /myapp/ { proxy_pass http://127.0.0.1:8000/; } + proxy_set_header X-Script-Name /myapp;
  • gunicorn を --root-path /myapp オプションで起動するパターンも有効(FastAPI 系と同様)
  • STATIC_URL / MEDIA_URL/myapp/static/ に合わせて変更が必須

「開始 URL を変更する」とは

Django アプリは通常ドメイン直下 https://example.com/ で配信しますが、運用都合でサブパス配信にしたいことがあります:

変更前: https://example.com/users/123
変更後: https://example.com/myapp/users/123
                          ^^^^^^^
                          このプレフィックスを追加

典型ケース:

  • 同一ドメインで複数アプリを共存(/billing / /inventory / /myapp
  • 既存サイトの一部として Django を組み込む
  • ALB / CloudFront のパスルーティング

方法 1: urls.py で include する

最もシンプルで Django ネイティブの方法。プロジェクトの urls.py でプレフィックスを付けます:

# config/urls.py(プロジェクトの urls.py)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('myapp/admin/', admin.site.urls),
    path('myapp/', include('app.urls')),       # ← サブパス配信
]

# app/urls.py(個別アプリの urls.py)
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),                  # → /myapp/
    path('users/', views.user_list, name='user_list'),    # → /myapp/users/
    path('users//', views.user_detail),           # → /myapp/users/123/
]

メリット: アプリ単独で完結し、Nginx 側を変えずに済む。
デメリット: テンプレートの {% url %}reverse() は OK だが、ハードコードしている URL を全部書き直す必要がある。

方法 2: FORCE_SCRIPT_NAME を設定する

アプリ内のコードはそのままで、Django にプレフィックスを意識させる方法:

# settings.py
FORCE_SCRIPT_NAME = '/myapp'
STATIC_URL = '/myapp/static/'
MEDIA_URL = '/myapp/media/'

# LOGIN_URL / LOGIN_REDIRECT_URL も影響する場合は調整
LOGIN_URL = '/myapp/accounts/login/'
LOGIN_REDIRECT_URL = '/myapp/'

これを設定すると {% url %}reverse() が自動的に /myapp/ を前置します。アプリ内のテンプレ・コードは元のままで OK。

方法 3: リバースプロキシで X-Script-Name ヘッダ

Nginx / Apache 側でヘッダを送り Django に教える方法。FORCE_SCRIPT_NAME を環境変数化したい場合に便利:

# /etc/nginx/sites-available/myapp.conf
server {
    listen 80;
    server_name example.com;

    location /myapp/ {
        proxy_pass http://127.0.0.1:8000/;     # ← 末尾スラッシュ重要

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Django にプレフィックスを伝える
        proxy_set_header X-Script-Name /myapp;
        proxy_set_header SCRIPT_NAME   /myapp;
    }

    # 静的ファイル
    location /myapp/static/ {
        alias /var/www/myapp/staticfiles/;
    }
}

Django 側で WSGI ミドルウェアを使うか、FORCE_SCRIPT_NAME で対応:

# wsgi.py
import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
application = get_wsgi_application()

# X-Script-Name 対応ミドルウェア
class ScriptNameMiddleware:
    def __init__(self, app):
        self.app = app
    def __call__(self, environ, start_response):
        script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
        if script_name:
            environ['SCRIPT_NAME'] = script_name
            path_info = environ.get('PATH_INFO', '')
            if path_info.startswith(script_name):
                environ['PATH_INFO'] = path_info[len(script_name):]
        return self.app(environ, start_response)

application = ScriptNameMiddleware(application)

gunicorn での起動

# 基本起動
gunicorn config.wsgi:application --bind 127.0.0.1:8000 --workers 4

# プレフィックスを Web サーバから受ける場合(推奨)
gunicorn config.wsgi:application \
    --bind 127.0.0.1:8000 \
    --workers 4 \
    --forwarded-allow-ips='127.0.0.1' \
    --proxy-allow-from='127.0.0.1'

# systemd ユニット例
# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp gunicorn
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/var/www/myapp/.venv/bin/gunicorn \
    --bind 127.0.0.1:8000 \
    --workers 4 \
    config.wsgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

uWSGI での起動

# /etc/uwsgi/apps-available/myapp.ini
[uwsgi]
project = myapp
base    = /var/www/myapp

chdir   = %(base)
home    = %(base)/.venv
module  = config.wsgi:application

# プレフィックスを WSGI に伝える
route-run = fixpathinfo:

# あるいは
mount = /myapp=config/wsgi.py
manage-script-name = true

master = true
processes = 4
socket = 127.0.0.1:8001
chmod-socket = 660
vacuum = true

Apache + mod_wsgi での同等設定


    ServerName example.com

    WSGIDaemonProcess myapp python-home=/var/www/myapp/.venv \
        python-path=/var/www/myapp
    WSGIProcessGroup myapp
    WSGIScriptAlias /myapp /var/www/myapp/config/wsgi.py

    Alias /myapp/static/ /var/www/myapp/staticfiles/
    
        Require all granted
    

    
        
            Require all granted
        
    

静的ファイルとメディアファイル

URL プレフィックスを変更したら、静的ファイルとメディアの URLも同じプレフィックスに揃える必要があります:

# settings.py
STATIC_URL = '/myapp/static/'
MEDIA_URL = '/myapp/media/'

STATIC_ROOT = '/var/www/myapp/staticfiles/'
MEDIA_ROOT  = '/var/www/myapp/media/'

# 反映
# $ python manage.py collectstatic --noinput

テンプレートでの URL 生成

{# ❌ ハードコードはサブパス変更で壊れる #}
ユーザ一覧


{# ✅ {% url %} と {% static %} を使う #}
{% load static %}
ユーザ一覧

確認方法

# プロジェクトの URL 一覧を表示
python manage.py show_urls           # django-extensions が必要

# Nginx 設定の確認
sudo nginx -T | grep -E "location|proxy_pass"

# 実際にアクセス
curl -I https://example.com/myapp/
curl -I https://example.com/myapp/static/logo.png

# gunicorn の起動確認
sudo journalctl -u myapp -f

よくあるトラブル

症状原因対処
静的ファイルが 404STATIC_URL がプレフィックス未対応STATIC_URL = '/myapp/static/' + collectstatic
リダイレクトが / に飛ぶFORCE_SCRIPT_NAME 未設定設定 + 再デプロイ
{% url %} が /myapp 含めないSCRIPT_NAME がリクエストに無いX-Script-Name ヘッダ送信
CSRF エラーCSRF_TRUSTED_ORIGINS 未設定setting に追加
proxy_pass で URL が二重末尾スラッシュの付け方proxy_pass http://127.0.0.1:8000/;

FAQ

Q: FORCE_SCRIPT_NAMEX-Script-Name どちらが推奨?
A: 環境別に切り替えるなら X-Script-Name(リバプロ任せ)、固定なら FORCE_SCRIPT_NAME。本番だけプレフィックス付けるなら後者がシンプル。

Q: 既存の絶対 URL を全部置換する必要はある?
A: テンプレ内の {% url %} / {% static %} 経由なら自動追従。ハードコード href="/users/"すべて書き直しが必要です。

Q: 管理画面 /admin/ もプレフィックス対象になるか
A: urls.py でまとめて path('myapp/', include(...)) していれば自動で /myapp/admin/ になります。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 環境構築とプロジェクト/アプリの作成
  2. MVC(MVT)のそれぞれの使い方と説明
  3. データベースへの接続と操作
  4. Django Administration
  5. git管理
  6. エラー一覧
  7. バージョンの確認方法
  8. ログ出力方法
  9. SQLのログ出力方法
  10. ログのローテート設定
  11. settings.pyの定数にアクセスする方法
  12. 本番環境へのインストールとアプリのデプロイ(apache編)
  13. 本番環境へのインストールとアプリのデプロイ(nginx編)
  14. djangoアプリの本番の開始URLを変更する
  15. 静的(static)ファイルの置き場所と読み込み(画像、css、js )
  16. CSRFトークンをAjaxで使用する方法
  17. ajaxの使用例(POST編)
  18. ファイルのアップロードとファイルの名前
  19. クイックスタート/チュートリアル
  20. ログイン機能
  21. テンプレート側のログイン判定
  22. ビュー側のログイン判定
  23. 管理者ユーザーの作成/判定と管理画面
  24. モデルのjson化とレスポンス
  25. runserverでポートを指定する方法
  26. cronによるバッチ実行
  27. テンプレートで利用する共通のcontextを定義する方法
  28. プログラムが本番サーバーで反映されない場合の対処法
  29. APIの作成
  30. cron用コマンド・ファイルの作成