CSRF verification fails in unexpected situations

I’m running Django 4.2.5 in a development environment and the CSRF middleware is not behaving as expected.

First, it was raising CSRF verification fail even when I knew the requests were being made from my own application. This happens inconsistently. Sometimes just by refreshing the page it works.

So I set the CSRF_TRUSTED_ORIGINS (only when DEBUG=True) to try to get it working. It still behave the same way, sometimes throwing the exception and sometimes not, for the same views.

The next thing I tried was to completely bypass the CSRF verification by commenting out the django.middleware.csrf.CsrfViewMiddleware. To my profound astonishment, it still raises the error even though I thought it should never even check the CSRF token in the first place.

I thought it was some problem with the development environment/settings and deployed my application to production and it still throws the exception inconsistently even when I know the request is coming from my website and is not a cross site forgery attempt.

Can anybody help?

You haven’t really provided much in the way of details regarding the views or templates involved, or whether or not there’s any JavaScript issuing the requests or if all requests are from Django-generated pages.

But there are a couple things to check.

First, if you’re talking about the Django admin showing this behavior, it is always going to use the CSRF protection.

Second, it’s possible that you have a second instance of your runserver running in the background that you’re not aware of. This would explain the erratic behavior on the same views. Check your processes to ensure you don’t have multiple copies of runserver running.

Thank you for your very fast answer.

I didn’t give details about the views and templates because it happens on many of them, so it didn’t seem to be related to the code or templates.

In the development environment it did happen very often in the admin, so this might explain it. I was running it on GitHub codespaces, so I thought the problem might be with the host or some setting in the container.

In production, however, I observed this in the login view. I am using Django authentication backend with the following view and custom template:

from django.contrib.auth import views as auth_views


urlpatterns = [
    path(
        'accounts/login/',
        auth_views.LoginView.as_view(),
        name='login'
    ),
    ...
]
{% extends "base.html" %}

{% load bootstrap5 %}

{% block title %}Login{% endblock title %}

{% block content %}
<div class="d-flex justify-content-center" style="margin-top: 1rem;">
    <div class="card" style="width: 36rem;">
        <div class="card-body">
            <h2 class="card-title text-center">Entrar na sua conta</h2>
            {% if next %}
                {% if user.is_authenticated %}
                <p>
                    Você não tem acesso a esta página. Para continuar, 
                    faça login com uma conta que tem acesso.
                </p>
                {% else %}
                <p>Por favor, faça o login para acessar esta página.</p>
                {% endif %}
            {% endif %}

            <form method="POST" action="{% url 'login' %}">
                {% csrf_token %}
                {% bootstrap_form form layout="horizontal" %}
                {% buttons %}
                    <input type="submit" class="btn btn-primary btn-block" value="Entrar">
                    <input type="reset" class="btn btn-primary btn-block" value = "Limpar">
                    <input type="hidden" name="next" value="{{ next }}">
                {% endbuttons %}
            </form>
            <p><a href="{% url 'password_reset' %}">Esqueci minha senha.</a></p>
        </div>
    </div>
</div>
{% endblock content %}

The behavior is still inconsistent. I try it once and it doesn’t work. Refresh the browser and it does.

There are a handful of different messages that get returned with this error. It would be helpful if you would try it a number of times, making note of which error is returned. (If it’s always the same error, this may be easier to track down than if you’re getting various messages.)

You may need to find the server logs to get the details if they’re not returned to the browser.

Hello, I’m sorry I took so long to answer. Here is the error message in development:

Reason given for failure:
CSRF token from POST incorrect.

Hi guys,

we’ve just had the same (or at least a similar) issue. After some inspection, we noticed, that the CSRF cookie sometimes wasn’t set. We were able to somewhat consistently reproduce the issue locally by following these steps:

  1. Open the page with the form on it
  2. Open DevTools (Chrome)
  3. In the Application Tab under “Cookies”, delete the “csrftoken” cookie, if present
  4. Refresh the page, and the “csrftoken” cookie doesn’t get set (which leads to the form submission failing). After a lot more refreshes the cookie eventually gets set again.

To us, it seemed like a caching problem, and indeed after we configured the cache to use the DummyCache, the “csrftoken” cookie started to consistently be set again (and the form submission worked as expected).

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.dummy.DummyCache",
    }
}

Might there be a solution that doesn’t involve using the DummyCache?