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 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's render engine replaces the {% csrf_token %} tag with a hidden input like:
⧉
1 | |
When the form is submitted:
- Token is sent 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 | |
This requests simply fetches all data from the 'products' endpoint. Nothing state-changing.
Example Unsafe POST:
⧉
1 | |
POST requests are meant to be state-changing an therefore need a token or the request gets blocked.
Basic Django Form Example:
⧉
1 2 3 4 5 6 | |
Without the token, Django usually returns:
403 Forbidden CSRF verification failed
Where Django Handles This
In its 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 → illegal request → request blocked.
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: The token required to make a state-changing request on your behalf is only accessible by you on the valid site and not the attacker on a malicious site.
Using CSRF with JavaScript / AJAX
Modern apps often submit requests with JavaScript instead of normal forms.
For JavaScript requests, the same principle applies: The CSRF token has to be sent to validate the request.
In .js scripts the token is sent as a header parameter X-CSRFToken
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 | |
Or by reading the cookies stored in the browser and retrieving the cookie from there.
A simple .js script to retrieve the crsf token from cookies can be found here: Get csrf-token from browser cookies
Common Mistakes
1. Forgetting Token in POST Form
⧉
1 | |
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.
Example:
⧉
1 2 3 4 5 | |
Sometimes legitimate for:
- Third-party webhooks
- External machine-to-machine endpoints
- APIs using token auth without cookies
Only use this decorator when truly justified and not just as a work-around.
4. Forgetting AJAX Headers
POST via JavaScript still needs token.
Include
⧉
1 2 3 4 | |
in your request
5. Removing Middleware
If CSRF middleware is disabled, protection disappears.
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.
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.