← Back to Django Course | Chapter 7: Django Forms | Lesson 7 of 11

CSRF Protection in Forms

CSRF protection is a secret handshake that makes sure a form submission really came from your own site, not a trick from somewhere else.
Syntax
markup
<form method="post">
  {% csrf_token %}
  <!-- form fields -->
  <button type="submit">Submit</button>
</form>

Why CSRF Protection Exists

Without it, a malicious site could trick a logged-in user's browser into submitting a form to your app without their knowledge.

Example: Why CSRF Protection Exists

Without it, a malicious site could trick a logged-in user's browser into submitting a form to your app without their knowledge.

markup
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Save</button>
</form>
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

What Happens Without the Token

A POST request missing the csrf token is rejected by Django's middleware with a 403 Forbidden response, before your view even runs.

Example: What Happens Without the Token

A POST request missing the csrf token is rejected by Django's middleware with a 403 Forbidden response, before your view even runs.

markup
# settings.py
MIDDLEWARE = [
    # ...
    'django.middleware.csrf.CsrfViewMiddleware',
    # ...
]
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

CSRF Token in AJAX Requests

For JavaScript-driven POST requests, read the token from the cookie or a hidden input and send it as the X-CSRFToken header.

Example: CSRF Token in AJAX Requests

For JavaScript-driven POST requests, read the token from the cookie or a hidden input and send it as the X-CSRFToken header.

markup
<script>
fetch('/save/', {
  method: 'POST',
  headers: {'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value},
  body: new FormData(document.querySelector('form'))
});
</script>
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Omitting {% csrf_token %} from a POST form, causing Django to reject the submission with a 403 Forbidden error.
  2. Disabling CSRF protection globally to fix the error instead of adding the missing token tag.
  3. Forgetting that AJAX POST requests also need the CSRF token sent as a header, not just template forms.
Chapter Summary
  • Django's CsrfViewMiddleware rejects POST requests that don't include a valid CSRF token.
  • Every template form that submits via POST must include {% csrf_token %} inside the <form> tag.
  • The token is unique per session, preventing other sites from forging requests to your app.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.