What is CSRF?
CSRF (Cross-Site Request Forgery) is a security mechanism that prevents unauthorized requests from being made on behalf of an authenticated user.
When a request is made to a protected endpoint without a valid CSRF token, the server rejects it, leading to the error:
403 Forbidden: CSRF token missing or incorrect.
Common Causes & Fixes
1️⃣ CSRF Token Missing in AJAX or API Request
Error Message:
403 Forbidden: CSRF token missing or incorrect.
If you’re making an API request via JavaScript (fetch, Axios, jQuery, etc.) and don’t send the CSRF token, Django/Flask rejects it.
Fix: Pass CSRF Token in Headers
- Django (Frontend Example)
Add the CSRF token from a cookie in JavaScript:function getCSRFToken() { return document.cookie.split('; ') .find(row => row.startsWith('csrftoken=')) ?.split('=')[1]; } fetch('/api/data/', { method: 'POST', headers: { 'X-CSRFToken': getCSRFToken(), 'Content-Type': 'application/json' }, body: JSON.stringify({ key: "value" }) });
- Flask (Frontend Example)
fetch('/api/submit', { method: 'POST', headers: { 'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').content, 'Content-Type': 'application/json' }, body: JSON.stringify({ data: "test" }) });
2️⃣ CSRF Token Not Included in HTML Form
Error Message:
403 Forbidden: CSRF token missing.
If you’re submitting a Django form, the CSRF token must be included.
Fix: Add {% csrf_token %}
Inside the Form
<form method="post">
{% csrf_token %}
<input type="text" name="name">
<button type="submit">Submit</button>
</form>
3️⃣ CSRF Middleware Not Enabled in Django
If CSRF protection is disabled, requests will fail unexpectedly.
Fix: Ensure Middleware is Enabled in settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware', # Ensure this is present
]
4️⃣ CSRF Exempt in Django for API Requests
If using APIs (React, Vue, Angular, etc.), Django’s CSRF protection might block non-browser requests.
Fix: Use @csrf_exempt
for APIs (if safe)
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
@csrf_exempt
def api_view(request):
return JsonResponse({"message": "Success"})
OR
Use Django REST Framework’s CSRF exemption:
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
@api_view(['POST'])
@permission_classes([AllowAny])
def api_view(request):
return JsonResponse({"message": "Success"})
5️⃣ CSRF Cookie Not Set or Expired
CSRF tokens are often stored in cookies, and missing/expired cookies can trigger this error.
✅ Fix: Ensure CSRF Cookie is Sent in Response
- Django (Ensure Cookie is Set)
Insettings.py
:CSRF_COOKIE_NAME = "csrftoken" CSRF_COOKIE_SECURE = False # Set True in production (HTTPS only)
Inviews.py
: pythonCopyEditfrom django.middleware.csrf import get_token from django.http import JsonResponse def set_csrf_token(request): return JsonResponse({"csrfToken": get_token(request)})
- Frontend Request Example (JavaScript)
fetch('/set-csrf-token/', { credentials: 'include' }) // Fetch token first .then(response => response.json()) .then(data => console.log("CSRF Token:", data.csrfToken));
6️⃣ CSRF Protection in Flask (Flask-WTF or Flask-SeaSurf)
Flask does not enable CSRF protection by default, but if you’re using Flask-WTF
or Flask-SeaSurf
, missing tokens can cause issues.
✅ Fix: Add CSRF Token in Flask Forms
from flask_wtf import FlaskForm
from wtforms import StringField
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)
class MyForm(FlaskForm):
name = StringField('Name')
- Include Token in HTML Forms
<form method="POST"> {{ form.hidden_tag() }} <!-- Includes CSRF token --> <input type="text" name="name"> <button type="submit">Submit</button> </form>
- Flask API Request (Allow JSON Requests)
from flask import Flask, request, jsonify from flask_cors import CORS from flask_wtf.csrf import CSRFProtect app = Flask(__name__) CORS(app) csrf = CSRFProtect(app) @app.route('/api/data', methods=['POST']) def api_data(): return jsonify({"message": "Success"})
7️⃣ CSRF Token Not Sent in Axios (React, Vue, etc.)
If using Axios for API calls, it may not send the CSRF token by default.
✅ Fix: Add CSRF Token in Headers
import axios from "axios";
axios.defaults.withCredentials = true;
axios.post('/api/data', { key: "value" }, {
headers: { 'X-CSRFToken': getCSRFToken() }
});
Debugging Tips
- Check if CSRF token is missing:
- Open DevTools → Network Tab → Click on the failed request → Check Headers.
- Ensure CSRF token is included in requests:
console.log(document.cookie); // Check if "csrftoken" exists
- Check Django logs for errors:
tail -f logs/error.log