Update #1 – 7/26/23: Per the comment by @thebjorn, I attempted to set the Content-Type: 'application/x-www-form-urlencoded
and set the token name to csrfmiddlewaretoken
but the error still persists.
For reference, here are the implementations I tested, incase I am missing anything:
- Adding the CSRF Token and Content-Type directly to the headers in the request:
headers: new Headers({
'Content-Type' : 'application/x-www-form-urlencoded',
'csrfmiddlewaretoken': csrftoken
})
- Appending the token to the form data:
coverPhotoForm.append('csrfmiddlewaretoken')
- Including both
csrfmiddlewaretoken
andX-CSRFToken
(seemed redundant but tested for continuity).
Original Post:
I am trying to use Django's CSRF protection when sending a dynamically created FormData
object, containing an image file, to a Django endpoint using JavaScript fetch
POST request. My issue is that I have been unable to successfully attach the csrf_token
to the request, which is causing a CSRF token missing
error.
Per Django's CSRF Documentation, I have included CSRF_COOKIE_HTTPONLY = True
in settings.py
, and then I am setting the hidden {% csrf_token %}
in my HTML file, and trying to access it in my JavaScript file:
// Create form to send image
let coverPhotoForm = new FormData();
// Read CSRF token and image file from DOM
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const coverPhotoUpload = document.querySelector('#cover-photo-upload');
// Add CSRF token and image to form object
coverPhotoForm.append('X-CSRFToken', csrftoken)
coverPhotoForm.append('cover_photo', coverPhotoUpload.files[0]);
// Add image via POST request to endpoint
fetch(`/profile/${profileUserId}`, {
method: 'POST',
body: coverPhotoForm,
mode: 'same-origin'
})
.then( response => response.json())
.then( response => {
console.log(response)
})
})
Notes:
csrftoken
seems to be read from the DOM successfully when checked viaconsole.log
.- When the page is refreshed, images are uploaded successfully, but the
CSRF token missing
error still occurs each time a request is sent.
I have also tried setting the headers directly within the fetch
request above, and using the Django docs example below, but that didn't change anything.
const request = new Request(
fetch(`/profile/${profileUserId},
{
method: 'POST',
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin'
}
);
Additional Attempts:
- My design above is adapted from the accepted answer from this SO post: Proper Django Fetch Validation.
- Tried Adding
accept
andcontent-type
keys to theheaders
dict in my request, suggested here: POST request Using JS/Django. - Added the
@ensure_csrf_cookie
decorator to myview
as suggested in the 'Warning' in the Django Docs link included above for dynamically created forms. - Used
csrfmiddlewaretoken: '{{ csrf_token }}'
(instead ofquerySelector
to read from DOM) per: Using CSRF Token in JS