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:
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!
Email sending timeouts are a common issue in production deployments and are likely caused by the email sending blocking the main request thread. Here’s how this can be fixed:
# Option 1: Using Django's send_mail_task
from django.core.mail import send_mail
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from asgiref.sync import async_to_sync
import asyncio
@ensure_csrf_cookie
async def form_submission_view(request):
try:
# Your form processing logic here
# Async email sending
asyncio.create_task(send_email_async(
subject="Your Subject",
message="Your Message",
from_email="[email protected]",
recipient_list=["[email protected]"]
))
return JsonResponse({'status': 'success'})
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
async def send_email_async(subject, message, from_email, recipient_list):
# Convert sync send_mail to async
await asyncio.to_thread(send_mail,
subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
fail_silently=False,
)
# Option 2: Using Celery for background tasks
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_email_task(subject, message, from_email, recipient_list):
send_mail(
subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
fail_silently=False,
)
@ensure_csrf_cookie
def form_submission_view_celery(request):
try:
# Your form processing logic here
# Queue email task
send_email_task.delay(
subject="Your Subject",
message="Your Message",
from_email="[email protected]",
recipient_list=["[email protected]"]
)
return JsonResponse({'status': 'success'})
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
# settings.py additions for Celery
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'UTC'
# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'your.smtp.server'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = '[email protected]'
EMAIL_HOST_PASSWORD = 'your-email-password'
DEFAULT_FROM_EMAIL = '[email protected]'
I’ve provided 2 main solutions to handle email sending in production:
To implement the Celery solution, you'll need to:
Install required packages:
pip install celery redis
Create a celery.py
file in your Django project:
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
app = Celery('your_project')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
Install and start Redis (if not already installed):
sudo apt-get install redis-server
sudo systemctl start redis
Start the Celery worker:
celery -A your_project worker -l info
Configure your Apache/WSGI setup to work with Celery:
# Add to your Apache configuration
WSGIDaemonProcess yourproject python-path=/path/to/virtualenv processes=2 threads=15
WSGIProcessGroup yourproject
Note: the email service (SMTP server) you’re using might affect the configuration needed.