I've seen a lot about this on SO, but nothing can fix my problem.
Problem:
With CSRF middleware enabled, Django responds with 403 on AJAX form request, stating:
"CSRF cookie not set."
Following the documentation, a JS functionality was implemented, that sets custom "X-CSRFToken" header.
It works as expected, gets "csrftoken" cookie from browser and posts it along with AJAX request:
x-csrftoken: 1a0u7GCQG0wepZHQNThIXeYpMy2lZOf2
But response is still 403.
Tried solutions:
I've tried everything I could find on SO or web, specifically:
Checking that middleware is enabled:
MIDDLEWARE_CLASSES = [
...
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
]
Different browsers with cookies enabled;
Decorating my view with @ensure_csrf_cookie
;
Setting {% csrf_token %}
in my template;
Using render
shortcut which takes right request context;
Setting custom CSRF_COOKIE_NAME
and CSRF_HEADER_NAME
in my settings.py
;
Explicitly setting CSRF_COOKIE_SECURE = False
and CSRF_COOKIE_HTTPONLY = False
;
Explicitly setting CSRF_TRUSTED_ORIGINS
setting;
Testing on development and production server;
Even request.META["CSRF_COOKIE_USED"] = True
in my view, as someone suggested.
And still got nothing.
Headers:
If I use @csrf_exempt
and print(request.META)
in my view, it's clear that custom header "X-CSRFToken" is present in request and formatted according to Django documentation, with "HTTP_" prefix, replaced hyphens with underscores, all uppercase: "HTTP_X_CSRFTOKEN".
Even more, it's value matches with cookie set by Django.
Cookies:
Strange thing is, if I try to print(request.COOKIES)
in my view, on page and form load I can see "csrftoken" cookie there, but dictionary is empty on AJAX request. Can it be the problem?
Desperate to find what is actually wrong. Thank you for reading this.
Ok, the issue is quite simple then:
Fetch API is not sending credentials by default. According to MDN:
The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests. This is similar to XHR’s withCredentials flag, but with three available values.
Default is omit
, and it never sends cookies. You just need to add same-origin
to your fetch()
function arguments:
fetch(formUrl, {
...
credentials: 'same-origin',
...
})
And you'll be good to go : )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With