Understanding csrf_token in Django: What It Is, Why It Exists, and How to Use It
Many developers first see csrf_token as a mysterious line copied from tutorials. In reality, it is a critical security feature protecting authenticated users from forged actions.
When working with forms in Django, one of the first template tags developers encounter is:
{% csrf_token %}
Many beginners add it because tutorials say they must, often without understanding what it does or why it matters. That token is not cosmetic. It is part of a critical web security mechanism designed to protect users and applications from Cross-Site Request Forgery (CSRF) attacks.
A full article on Django's templatetags can be found here: Django's Basic Templatetags
This article explains:
- What CSRF is
- Why csrf_token exists
- How Django uses it
- How to use it in forms and JavaScript
- Common mistakes
- Best practices
What Is CSRF?
CSRF stands for: Cross-Site Request Forgery
It is an attack where a malicious website tricks a logged-in user’s browser into sending unwanted requests to another trusted site.
Example Scenario
Imagine a user is logged into:
bank.com
Their browser stores authentication cookies.
Now the user visits a malicious site containing:
⧉
1 2 3 4 5 6 7 8 | |
The browser may automatically send:
- Session cookies
- Authentication cookies
So the trusted site thinks the request came from the real user. That is CSRF.
A full article covering CSRF attacks in detail -> CSRF attacks
Why Cookies Cause This
Browsers automatically attach cookies to requests for matching domains. That is convenient for authentication—but dangerous if requests can be forged.
Why csrf_token Exists
Django prevents forged requests by requiring a secret token that:
- The attacker cannot guess
- Must be submitted with unsafe requests
- Must match the user's session/browser state
If the token is missing or invalid, Django blocks the request.
The Django Solution
In templates:
⧉
1 2 3 4 | |
Django inserts a hidden input like:
⧉
1 | |
When the form is submitted:
- Token goes with request body
- Django verifies it
- If valid → request allowed
- If invalid → request rejected
Why GET Usually Does Not Need It
CSRF protection typically applies to state-changing requests:
- POST
- PUT
- PATCH
- DELETE
Not usually: - GET
Because GET should only retrieve data, never modify it.
Example Safe GET:
⧉
1 | |
Example Unsafe POST:
⧉
1 | |
POST request needs a token or request gets blocked.
Basic Django Form Example:
⧉
1 2 3 4 5 6 7 8 | |
Without the token, Django usually returns:
403 Forbidden CSRF verification failed
Where Django Handles This
Through middleware:
'django.middleware.csrf.CsrfViewMiddleware'
Usually enabled by default in settings.py
How Validation Works (Simplified)
Django checks:
- Token exists
- Token matches expected secret
- Request origin/referrer rules (HTTPS contexts)
- Request method requires protection
If checks fail → block request.
Why Attackers Cannot Easily Fake It
Attackers can cause requests to be sent, but they usually cannot read your trusted site’s pages due to browser same-origin policy. That means they cannot easily obtain the valid CSRF token. That is the defense model.
Using CSRF with JavaScript / AJAX
Modern apps often submit requests with JavaScript instead of normal forms.
Example with fetch():
⧉
1 2 3 4 5 6 7 8 9 10 | |
Where to Get the Token
Often from cookie or template output.
Example in template:
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
No token → 403 error.
2. Using GET for Data Modification
Bad:
⧉
1 | |
Use POST/DELETE with CSRF protection.
3. Disabling CSRF to "Fix Errors"
Django provides a decorator that can be used to mark certain endpoints as 'csrf exempt', meaning that even for state modifying requests no csrf token is required.
Bad habit is to use:
⧉
1 2 3 | |
if POST requests cause errors.
Only use this decorator when truly justified.
4. Forgetting AJAX Headers
POST via JavaScript still needs token.
5. Removing Middleware
If CSRF middleware is disabled, protection disappears.
What @csrf_exempt Does
Django allows:
⧉
1 2 3 4 5 | |
Sometimes legitimate for:
- Third-party webhooks
- External machine-to-machine endpoints
- APIs using token auth without cookies
Use carefully, and only when truly justified.
Django Is Strong in terms of Security
Django has long prioritized secure defaults.
By default it gives:
- CSRF middleware
- Template tag support
- Secure cookie integrations
- Clear failure behavior
This is one reason Django is respected for security.
Without CSRF protection:
- Account changes can be forged
- Purchases can be triggered
- Messages can be posted
- Sensitive actions can be abused
With protection:
- Requests need trusted token context
csrf_token function as proof that the form/request was generated by your real Django site, not a malicious third-party page.
Best Practices
Always Include in POST Forms
{% csrf_token %}
Keep Middleware Enabled
Default Django config is usually correct.
Use Proper HTTP Methods
- GET = read
- POST = modify
Handle AJAX Correctly
Send X-CSRFToken.
Avoid csrf_exempt Unless Necessary
Security exceptions should be deliberate.
Join the Newsletter
Practical insights on Django, backend systems, deployment, architecture, and real-world development — delivered without noise.
Get updates when new guides, learning paths, cheat sheets, and field notes are published.
No spam. Unsubscribe anytime.
There is no third-party involved so don't worry - we won't share your details with anyone.