| この記事の要点 |
|
エラー内容
Django でフォームを送信(POST)すると、次の画面が表示されて処理が止まります。
Forbidden (403)
CSRF verification failed. Request aborted.
CSRF(クロスサイトリクエストフォージェリ)は、別サイトから利用者のブラウザを使って勝手に POST させる攻撃です。Django は標準で有効な CsrfViewMiddleware により、POST・PUT・PATCH・DELETE のリクエストに正しいトークンが付いているかを検査し、付いていなければこのエラーで拒否します。GET では発生しません。
まず原因を確認する
settings.py で DEBUG = True の状態なら、エラー画面の下部に Reason given for failure として拒否理由が表示されます。主なものは次のとおりです。
| 表示される理由 | 主な原因 | 対処 |
|---|---|---|
| CSRF token missing. | フォームに {% csrf_token %} がない、Ajax でヘッダーを送っていない | 対処 1・2 |
| CSRF cookie not set. | ブラウザに csrftoken クッキーがない(クッキーのブロック、HTTPS 専用設定を HTTP で使っている等) | 対処 4 |
| Origin checking failed - ... does not match any trusted origins. | 送信元のオリジンが信頼済みとして登録されていない | 対処 3 |
| CSRF token incorrect / has incorrect length 等 | 古い画面から送信した、トークンの値が壊れている | 画面を再読み込みして再送信 |
対処 1: form に {% csrf_token %} を書く
テンプレート側の form タグ内に csrf_token を設定していないことが、最も多い原因です。POST するすべての form に入れます。
<form method="post" action="{% url 'contact' %}">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">送信</button>
</form>
{% csrf_token %} は、トークンを入れた隠しフィールド(<input type="hidden" name="csrfmiddlewaretoken" ...>)に展開されます。ブラウザの「ページのソースを表示」でこの行が出力されているか確認してください。
書いたのに空になる場合
ビューでテンプレートを描画するときに request を渡していないと、トークンが出力されません。render() を使えば自動で渡されます。
from django.shortcuts import render
def contact(request):
# 第 1 引数に request を渡すことでトークンが使えるようになる
return render(request, "contact.html", {"form": form})
対処 2: Ajax / fetch ではヘッダーで送る
JavaScript から POST する場合は、クッキー csrftoken の値を X-CSRFToken ヘッダーに入れて送ります。
function getCookie(name) {
const m = document.cookie.match('(^|;)\\s*' + name + '=([^;]*)');
return m ? decodeURIComponent(m[2]) : null;
}
fetch("/api/save/", {
method: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "test" }),
});
ページ内に一度も {% csrf_token %} がないとクッキーが発行されないことがあります。その場合はビューに @ensure_csrf_cookie デコレーターを付けます。詳しくは Django で CSRF トークンを Ajax 送信する方法 を参照してください。
対処 3: CSRF_TRUSTED_ORIGINS を設定する(HTTPS・プロキシ配下)
Django 4.0 以降は、リクエストの Origin ヘッダーも検査します。nginx などのリバースプロキシ経由で HTTPS 公開している場合や、別ドメインのフロントエンドから送信している場合は、信頼するオリジンをスキーム付きで登録します。
# settings.py
CSRF_TRUSTED_ORIGINS = [
"https://example.com",
"https://www.example.com",
]
Django 3.x までの書き方("example.com" のようにスキームなし)のまま 4.0 以降にアップグレードすると、このエラーが出るようになります。プロキシ配下では、プロキシが X-Forwarded-Proto を渡し、settings.py の SECURE_PROXY_SSL_HEADER でそれを信頼する設定も確認してください。
対処 4: CSRF クッキーが届いているか確認する
CSRF_COOKIE_SECURE = Trueのまま HTTP(http://localhost など)でアクセスすると、クッキーが送られず「CSRF cookie not set.」になります。開発環境では False にします- ブラウザのクッキーブロック設定や、iframe 内での表示(サードパーティクッキー扱い)でも発生します
- ブラウザの開発者ツールの「Application(ストレージ)」→「Cookies」で
csrftokenがあるか確認します
やってはいけない対処
エラーを消すために settings.py の MIDDLEWARE から CsrfViewMiddleware を削除したり、すべてのビューに @csrf_exempt を付けたりするのは避けてください。CSRF 攻撃に無防備になります。@csrf_exempt は、外部サービスからの Webhook 受信など、署名検証など別の手段で正当性を確認できるビューに限って使います。
確認方法
- ページのソースに
csrfmiddlewaretokenの hidden フィールドがあるか - 開発者ツールの Network タブで、POST リクエストにトークン(フォームデータまたは X-CSRFToken ヘッダー)と csrftoken クッキーが付いているか
- DEBUG=True のエラー画面の Reason が変わったか(原因が 1 つ解消されると別の理由に変わることがある)
関連
- Django で CSRF トークンを Ajax 送信する方法|X-CSRFToken ヘッダー
- Django + Ajax POST 通信完全ガイド(CSRF Token / JsonResponse / fetch / DRF)
- djangoでGETとPOSTパラメータ受け取る方法
- Laravel「The page has expired due to inactivity」の原因と対処(CSRF / セッション切れ)
子ページはありません
- Invalid HTTP_HOST header: '...'. You may need to add '...' to ALLOWED_HOSTS
- CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
- django.utils.datastructures.MultiValueDictKeyError
- Forbidden (403) CSRF verification failed. Request aborted.
- ModuleNotFoundError: No module named 'MySQLdb'
- WARNINGS: ?: (mysql.W002) MySQL Strict Mode is not set for database connection
- Unknown column 'table_name.id' in 'field list'
- RuntimeError: Model class ~ doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
- get() returned more than one MynumberRegist -- it returned 2!
- django.db.utils.OperationalError: (2006, "Can't connect to MySQL server")
- 'include' is not defined
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Djangoテンプレートのif・elif・else|型・未定義と複合条件 2026-10-25 23:02:54
- DjangoのSQLite接続設定|DATABASES・dbshellとCLIエラーの確認 2026-10-25 22:56:56
- Django REST FrameworkでAPIを作る|URL接続・入力検証・所有者の認可 2026-10-25 21:03:21
- DjangoモデルをJSONで返す|型・公開フィールド・DRF認可 2026-10-25 20:46:35
- precompile standard library とは(Pythonインストール) 2026-10-25 20:30:17
- ANACONDA NAVIGATORの起動方法 2026-10-25 20:30:17
- DjangoのAjax POST|CSRF・JSON入力検証・DRFの認可 2026-10-25 20:30:16
- Django/MySQLのUnknown column ...id|主キー・実列・別名の確認 2026-10-25 20:08:53
- 【django】limit, offsetの指定方法 2026-10-25 20:08:51
- Djangoの本番で変更が反映されないとき|プロセス・テンプレート・静的配信の確認 2026-10-25 19:40:35
- Django のファイルアップロード|ファイル名と拡張子の取得 2026-10-25 19:40:35
- djangoにおけるHTTPステータスコードの返し方 2026-10-25 19:40:34
- PythonのResponseオブジェクトの中身の確認 2026-10-25 19:17:54
- Python で Cookie を扱う|requests の Session と http.cookies 2026-10-25 19:17:54
- Python で HTTP リクエスト|requests・urllib・httpx と curl の対応 2026-10-25 19:17:53