Javascript fetch return 403 on Apache Server
Struggling with this issue for a few days now so any help is greatly appreciated.
I managed to deploy my django project on a Linux server and it works fine apart from the model form submit. On the local development server it work fine and here is the workflow:
- User fills the form;
- User clicks submit;
- Javascript catches the submit event, submits a "POST" request and gets a response back;
- On the server side, the form is checked and if all is good an email is sent;
- HTML is updated to add the confirmation of form registration;
Here is my code:
home.html
<div class="col-lg-8 offset-lg-2">
<form method="POST" class="row mt-17" id="form" onsubmit="return false">
{% csrf_token %}
{% for field in form %}
{% if field.name not in "message" %}
<div class="col-12 col-sm-6">
<div class=form-group>
<label for={{field.name}} class="form-label">{{ field.label }}</label>
{{ field }}
</div>
</div>
{% else %}
<div class="col-12">
<div class=form-group>
<label for={{field.name}} class="form-label">{{ field.label }}</label>
{{ field }}
</div>
</div>
{% endif %}
{% endfor %}
<div class="form-group mb-0">
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
</div>
</form>
</div>
main.js
const form = document.getElementById('form');
form.addEventListener("submit", submitHandler);
function submitHandler(e) {
e.preventDefault();
fetch("{% url 'messages-api' %}", {
credentials: "include",
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new FormData(form)
})
.then(response => response.json())
.then(data => {
alert("Got your message")
})
}
ssl.conf file
<Directory /home/admin/pinpoint/templates>
Require all granted
</Directory>
Alias /static /home/admin/pinpoint/static
<Directory /home/admin/pinpoint/static>
Require all granted
</Directory>
Alias /media /home/admin/pinpoint/media
<Directory /home/admin/pinpoint/media>
Require all granted
</Directory>
<Directory /home/admin/pinpoint/project>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIScriptAlias / /home/admin/pinpoint/project/wsgi.py
WSGIDaemonProcess pinpoint python-path=/home/admin/pinpoint python-home=/home/admin/pinpoint/venv
WSGIProcessGroup pinpoint
On the development server, whenever you click submit, I get 403 (Forbidden) in the console.
Since the development version works fine, my guess would be it's a permission issue. As such, I gave apache ownership and r+w rights in my templates folder. Still the issue persists.
If i remove the headers content in the fetch command, the form is registered in the database but after ~2 minutes I get Server Error(500).
Any help/suggestions are welcome.
The issue is likely related to CSRF protection and a possible improper handling of the form data. I made some changes and added additional steps to help resolve the problem as seen below:
// --- Updated form submission code ---
// main.js
const form = document.getElementById('form');
form.addEventListener("submit", submitHandler);
async function submitHandler(e) {
e.preventDefault();
// Get the CSRF token from the cookie
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const formData = new FormData(form);
try {
const response = await fetch("{% url 'messages-api' %}", {
method: 'POST',
credentials: 'same-origin', // Important for CSRF
headers: {
'X-CSRFToken': csrftoken,
// Don't set Content-Type when using FormData
},
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
alert("Got your message");
} catch (error) {
console.error('Error:', error);
alert("There was an error submitting the form. Please try again.");
}
}
The 403 Forbidden error occurs when Django’s CSRF protection blocks the request so I updated the code to properly include the CSRF token in the request headers. Also, using credentials: ‘same-origin’
ensures cookies are sent with the request
‘Content-Type’
header was removed because when using FormData
, the browser needs to set this automatically. This allows the browser to properly set the boundary for multipart form data.
Add these lines to your Apache configuration (ssl.conf) to handle CORS and CSRF:
# Add to your ssl.conf
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "Content-Type, X-CSRFToken"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
See to it that these Django settings are properly configured in your settings.py:
CSRF_COOKIE_SECURE = True # for HTTPS
CSRF_COOKIE_HTTPONLY = False # Allows JavaScript to read the cookie
CSRF_TRUSTED_ORIGINS = ['https://yourdomain.com'] # Add your domain
Check these permissions on your server:
# Set appropriate permissions for Apache
sudo chown -R www-data:www-data /home/admin/pinpoint
sudo chmod -R 755 /home/admin/pinpoint
# Make sure media and static directories are writable
sudo chmod -R 775 /home/admin/pinpoint/media
sudo chmod -R 775 /home/admin/pinpoint/static
To figure out why you’re getting a 500 error after 2 minutes:
Check your Apache error logs: /var/log/apache2/error.log
Check your Django logs
Temporarily set DEBUG = True
in settings.py to see detailed error messages
Add proper error handling in your Django view:
# views.py
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def your_view(request):
try:
# Your existing view logic here
return JsonResponse({'status': 'success'})
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
You may additionally check:
The browser’s developer tools Network tab to see the exact request/response
Look at the Apache and Django logs for specific error messages
Confirm if there are any timeouts configured in your server that might be causing the 2 minute delay
I hope this helps!