同じ URL は GET では正常にアクセスできるのに、POST でデータを送信すると 403 エラーが返る場合、たいていは Django の CSRF 検証に通っていないことが原因です。
Django ではデフォルトで CSRF 保護が有効になっています。プロジェクトで次の設定が有効になっている場合:
'django.middleware.csrf.CsrfViewMiddleware',
POST リクエストには正しい CSRF token を含める必要があります。そうでない場合、Django はリクエストを拒否し、403 を返します。
推奨方法:CSRF token を送信する
通常のフォームであれば、テンプレート内の <form> タグの中に次を追加します:
<form method="post">
{% csrf_token %}
<!-- form fields -->
<button type="submit">Submit</button>
</form>
これにより、Django はページ内に hidden フィールドを生成します。POST 時にその値も一緒に送信されるため、CSRF 検証を通過できます。
Ajax リクエストの場合は、CSRF token をリクエストヘッダーに入れる必要があります。一般的には cookie から csrftoken を読み取り、POST リクエストと一緒に送信します:
fetch('/your-url/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify({ name: 'value' })
})
方法 1:CSRF ミドルウェアを無効にする
settings.py で次の行をコメントアウトできます:
'django.middleware.csrf.CsrfViewMiddleware',
こうすると、POST リクエストが CSRF 検証の失敗によって 403 を返すことはなくなります。
ただし、これはプロジェクト全体の CSRF 保護を無効にするため、本番環境では推奨されません。ローカルテストや、用途が非常に明確な内部インターフェースの場合を除き、CSRF token を使う方法を優先すべきです。
方法 2:単一のビューだけ CSRF 検証を無効にする
特定のインターフェースだけ CSRF 検証が不要な場合は、view の request handler の前に @csrf_exempt を付けます:
# views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def index(request):
return HttpResponse('ok')
この方法は、デコレーターを付けたビューにだけ影響するため、ミドルウェア全体を無効にするより範囲が小さくなります。ただし、そのインターフェースは CSRF 保護をスキップすることになるため、本当に必要な場合にのみ使うべきです。たとえば、サードパーティのコールバック用インターフェース、内部サービス用インターフェース、または別の認証・署名メカニズムを備えた API などです。
