Javascript fetch возвращает 403 на сервере Apache

Боюсь с этой проблемой уже несколько дней, поэтому любая помощь будет очень признательна.

Мне удалось развернуть свой проект django на сервере Linux, и он работает нормально, за исключением отправки формы модели. На локальном сервере разработки все работает нормально, и вот рабочий процесс:

  1. Пользователь заполняет форму;
  2. Пользователь нажимает кнопку submit;
  3. Javascript перехватывает событие submit, отправляет запрос «POST» и получает ответ;
  4. На стороне сервера форма проверяется и, если все в порядке, отправляется письмо;
  5. HTML обновляется, чтобы добавить подтверждение регистрации формы;

Вот мой код:

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

    <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

На сервере разработки при нажатии кнопки submit я получаю 403 (Forbidden) в консоли.

Поскольку версия для разработки работает нормально, я предполагаю, что это проблема с правами. Поэтому я дал apache права на владение и r+w в папке templates. Но проблема не исчезла.

Если я удаляю содержимое заголовков в команде fetch, форма регистрируется в базе данных, но через ~2 минуты я получаю Server Error(500).

Любая помощь/предложения приветствуются.

Проблема, скорее всего, связана с защитой CSRF и возможной неправильной обработкой данных формы. Я внес некоторые изменения и добавил дополнительные шаги, чтобы помочь решить проблему, как показано ниже:

// --- 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.");
    }
}

Ошибка 403 Forbidden возникает, когда CSRF-защита Django блокирует запрос, поэтому я обновил код, чтобы правильно включить CSRF-токен в заголовки запроса. Кроме того, использование credentials: ‘same-origin’ гарантирует, что куки будут отправлены вместе с запросом

<<<Заголовок

‘Content-Type’ был удален, потому что при использовании FormData браузер должен устанавливать его автоматически. Это позволяет браузеру правильно установить границу для многочастных данных формы.

Добавьте эти строки в конфигурацию Apache (ssl.conf) для обработки CORS и 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"

Убедитесь, что эти параметры Django правильно настроены в файле 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

Проверьте эти разрешения на вашем сервере:

# 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

Чтобы выяснить, почему вы получаете ошибку 500 через 2 минуты:

Проверьте журналы ошибок Apache: /var/log/apache2/error.log

Проверьте журналы Django

Временно установите DEBUG = True в settings.py, чтобы видеть подробные сообщения об ошибках

Добавьте правильную обработку ошибок в представление Django:

# 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)

Вы можете дополнительно проверить:

  • На вкладке Сеть инструментов разработчика браузера можно увидеть точный запрос/ответ

  • Просмотрите журналы Apache и Django на предмет конкретных сообщений об ошибках

    .
  • Проверьте, не настроены ли на вашем сервере какие-либо таймауты, которые могут быть причиной 2-минутной задержки

    .

Надеюсь, это поможет!

Вернуться на верх