2025, Dec 20 09:02

Internal Server Error при деплое Django на cPanel с Apache/mod_wsgi: виноват Python

Как мы исправили Internal Server Error (AH00124) при деплое Django на cPanel с Apache/mod_wsgi: проблема в сборке Python и C‑расширениях, а не в Apache.

Развертывание Django на cPanel с Apache/mod_wsgi: поиски Internal Server Error, который оказался не в Apache

Развертывание приложения Django на сервере cPanel с Apache и mod_wsgi кажется простым — пока внезапно не перестает таким быть. В нашем случае проявлялась упрямая «Внутренняя ошибка сервера» (Internal Server Error) вместе с сообщением Apache о превышении лимита внутренних перенаправлений. Окружение состояло из двух изолированных инстансов Django — production и staging — с отдельными базами данных и env‑файлами. Настройка Apache/mod_wsgi была выполнена вручную, поскольку в аккаунте cPanel не было функции Setup Python App. До продления сервера и переконфигурирования баз приложение работало, но после этого оба домена падали с одной и той же ошибкой.

Журнал ошибок Apache был однозначен в одном: сервер считал, что попал в цикл. Он выдавал хорошо известное сообщение AH00124. Файлов .htaccess с правилами не было, а собственные логи приложения либо пустовали, либо не успевали ловить что‑то полезное, что намекало: запрос даже не доходит до подсистемы логирования Django.

Как была устроена конфигурация

Развертывание опиралось на Direct Include Style в cPanel, когда httpd.conf подключает дополнительные пользовательские конфиги. Ниже — ключевые элементы: подключение WSGI‑приложений, маршрутизация статики и WSGI‑входная точка, которая читает переменные окружения и выставляет корректный модуль настроек Django. Имена отличаются от оригинала, но поведение то же.

ServerName example.in
ServerAlias www.example.in
ServerAlias 203.0.113.10
WSGIDaemonProcess svc_main python-home=/home/user/app/venv python-path=/home/user/app user=apacheuser group=appgroup
WSGIProcessGroup grp_main
WSGIScriptAlias / /etc/validator/wsgi/prod.wsgi 
Alias /static/ /home/user/app/staticfiles/
<Directory /home/user/app/staticfiles>
    Require all granted
</Directory>
<Directory /etc/validator/wsgi>
    <Files prod.wsgi>
        Require all granted
    </Files>
</Directory>
ErrorLog /home/user/app/error.log
CustomLog /home/user/app/access.log combined

Стендовая инстанция повторяла подход с собственным venv, путями и WSGI‑файлом.

ServerName test.example.in
ServerAlias www.test.example.in
WSGIDaemonProcess svc_stage python-home=/home/user/app_stage/venv python-path=/home/user/app_stage user=apacheuser group=appgroup
WSGIProcessGroup grp_stage
WSGIScriptAlias / /etc/validator/wsgi/stage.wsgi 
Alias /static/ /home/user/app_stage/staticfiles/
<Directory /home/user/app_stage/staticfiles>
    Require all granted
</Directory>
<Directory /etc/validator/wsgi>
    <Files stage.wsgi>
        Require all granted
    </Files>
</Directory>
ErrorLog /home/user/app_stage/error.log
CustomLog /home/user/app_stage/access.log combined

WSGI‑входная точка подгружала нужный env‑файл и выбирала целевой модуль настроек.

import os as osmod
import sys as sysmod
from pathlib import Path as Pth
from dotenv import load_dotenv as loadenv
ENV_PATH = Pth('/etc/validator/envs/production.env')
loadenv(dotenv_path=ENV_PATH)
osmod.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coreapp.settings.production')
from django.core.wsgi import get_wsgi_application as get_app
application = get_app()

Продакшен‑настройки использовали переменные окружения для подключения к базе данных и отключали DEBUG. Имена в Django оставлены как есть; импорты и локальные имена адаптированы без изменения поведения.

import os as osmod
from .base import *  # noqa
DEBUG = False
ALLOWED_HOSTS = [
    'example',
    'www.example',
    '203.0.113.10',
]
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlbackend',
        'USER': osmod.environ['DBUSER'],
        'PASSWORD': osmod.environ['DBPASSWORD'],
        'NAME': osmod.environ['DBNAME'],
        'HOST': osmod.environ['DBHOST'],
        'PORT': osmod.environ['DBPORT'],
    }
}

Что на самом деле говорил сервер

Apache многократно сообщал, что превышен лимит рекурсии для внутренних перенаправлений.

AH00124: Запрос превысил лимит из 10 внутренних перенаправлений из-за вероятной ошибки конфигурации. При необходимости используйте 'LimitInternalRecursion', чтобы увеличить лимит. Установите 'LogLevel debug', чтобы получить обратную трассировку.

Без RewriteRule в .htaccess и без какой‑либо пользовательской логики перенаправлений в показанных фрагментах это указывало на цикл на веб‑уровне, в то время как приложение молчало. В этом и заключался парадокс: Apache был уверен, что зациклился, а Django не оставлял ни следа.

Корневая причина

Несмотря на «редиректные» подсказки в логах, истинной проблемой оказалась сборка Python. Некоторые модули с C‑расширениями, в том числе используемые pickle, были собраны некорректно. Из‑за этого рантайм под mod_wsgi ломался и приводил к Internal Server Error. Корректная пересборка окружения устранила сбой.

Как это исправили

Решение — пересобрать Python‑окружение так, чтобы необходимые C‑расширения корректно компилировались. Менять блоки vhost в Apache, WSGI‑входные точки или настройки Django не потребовалось для устранения проблемы.

Почему это важно для Django на cPanel с mod_wsgi

Сообщения инфраструктуры могут вводить в заблуждение, если рантайм приложения падает, не успев ничего залогировать. Можно увидеть предупреждение AH00124 о редиректах и при этом столкнуться с проблемой на более низком уровне — в интерпретаторе Python или его расширениях. Когда WSGI‑процесс гибнет на старте, Apache может отдать общий Internal Server Error, а контекст вокруг будет намекать на ошибку в HTTP‑настройках, хотя истинная причина — в сборке Python.

Это особенно актуально, когда вы запускаете несколько инстансов Django с отдельными виртуальными окружениями, раздельными WSGI‑файлами и доменными include‑конфигами Apache. Каждое окружение должно быть здорово само по себе. Если после продления сервера или перекомпоновки зависимостей меняются исходные предпосылки о Python‑тулчейне, легко начать отлаживать не тот уровень.

Итоги и заключение

Если при деплое на mod_wsgi вы ловите Internal Server Error, а Apache утверждает, что упирается в предел внутренних перенаправлений, не ограничивайтесь проверкой веб‑серверных правил. Проверьте, что Python‑рантайм и C‑расширения, от которых зависит ваш стек, собраны корректно. В конфигурациях с cPanel, где вы вручную настраиваете WSGI‑демоны и доменные конфиги, веб‑слой может быть в порядке, а интерпретатор под ним — нет. После пересборки Python‑окружения и правильной компиляции расширений развертывание снова заработало.