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?

I have the same problem too. I was using the Redis cache backend and ran in to csrf issues randomly. With the dummy cache, it seems to work again properly. Strange behaviour!

Just an update to my prior post: I’m using Redis for caching. I was able to resolve the problem by increasing the memory allocated to Redis. I don’t know if it’s related to out-of-memory issues, but after changing this, I haven’t experienced any issues. Hope it helps. Thanks.

I don’t have any cache backend defined and I’m facing this issue several times per day. I was able to reproduce it locally, by clicking multiple times on the button to submit the same form. Does it make sense? If yes, what could be a possible solution for this?

Hello! We are facing the same issue. We have encountered two types of CSRF errors.

The first is most concerning: “CSRF token from POST incorrect”, where I believe the CSRF token in the POST request is not matching the one in the cookie. It happens to users who are already logged in.

The second one is “CSRF cookie not set”. I think that could be remedied by adding ensure_csrf_cookie to all affected views. Maybe even a middleware, but I’m not sure that’s a good idea performance wise.

We have two Django containers for blue-green deployment, each with multiple Gunicorn workers. We also auto-expire the sessions. We don’t have any caches.

Does anybody know a solution or has any pointers?

I’m using Codespaces for the cs50W course from EdX, and this issue is pernicious. I’m experiencing the same kind of issues with tokens not working in a simple login form, or the csrf cookie not being set. I consulted several AIs, and eventually Github Copilot suggested I try running my project local to my PC, after we’d troubleshooted settings.py and forced headers not to be cached, and tokens being rotated on logout in the view. Guess what? All works well locally, but at Codespaces, logging out and refreshing a non-login page just randomly shows a different user being logged in, or a host of other strange behavior.

I’m just going to code locally from now on as this issue seems to be a Codespace issue, is complete BS, and has cost me countless hours trying to fix.

Is there any foolproof way of using csrf tokens in forms (beyond NOT using them and trying another solution) that ought to work with most Django enabled webhosts?

Are there any low-level tutorials (b/c I’m very new to Django) anyone can think of that can show me how to use another, more bulletproof, method to handle login/logout/protected views?

Thank you all very much, in advance!

-Joe L.

Welcome @josephlevin !

I can’t address any of the issues you’ve reported with Codespaces, but I can tell you that I have never personally experienced those behaviors.

I do most of my development now on a Windows 11 laptop using VSCode, with the code residing either in a WSL environment on that same laptop, or one one of my servers in my test lab, or on one of the development servers at work.

As long as I keep my browser environments isolated, (don’t try to log on with different users in different tabs of the same browser environment) every works perfected as described in the docs.

The issue seems like it only appears on Codespaces. As soon as I switched to developing locally, all was well. I’m using VS Code on a Windows 10 laptop, atm. I’d love to run WSL, but man, the one time I tried doing it on my desktop PC to play with Docker and WordPress containers, the system became so unstable I had to reimage my drive. It was a terrible experience.

Anyway, I never tried to login from different tabs in the same browser, but doing so from different browsers, or trying to login/out from the same tab in Chrome (again, while using Codespaces to webserve the project), things went very wonky with csrf. I think I’m going to skip using Codespaces except to upload the finished project and submit it to the course.

I suppose I shouldn’t grouse as I have a means to develop now. I’m just wondering what other sort of gotchas while using simple logins/logouts with protected views I’m going to face if I ever deploy to a public facing webhost.