Что означает 403 в Apache и почему это почти всегда «настройка доступа»
Сообщение 403 Forbidden (часто как «You don't have permission to access / on this server») означает: Apache запрос получил, но сознательно отказал в доступе. Это принципиально отличается от 404: при 404 ресурса/маршрута нет, при 403 он есть, но для него не выполняются правила доступа или ограничения безопасности.
Практически все причины apache 403 укладываются в четыре группы:
- директивы авторизации/доступа Apache 2.4:
Requireи контейнеры<Directory>,<Location>,<Files>(включая наследование и переопределения); - права файловой системы: владелец/группа, режимы, и особенно «x» на каталогах по всей цепочке;
- надстройки запуска:
suexecи требования к владельцу/правам для CGI/скриптов; - MAC-политики:
SELinuxдляhttpd(реже AppArmor).
Дальше — порядок проверки, который обычно дает причину 403 за 10–15 минут.
Шаг 1. Сразу смотрим логи: там обычно написана причина
При 403 лучше не «гадать по конфигу». В error log почти всегда есть формулировка, которая указывает, это правило Require, права/контекст или suEXEC. На SELinux-системах дополнительно полезен audit log.
Где искать error log
Пути зависят от дистрибутива и настроек vhost. Частые варианты:
- Debian/Ubuntu:
/var/log/apache2/error.log - RHEL/Alma/Rocky/CentOS:
/var/log/httpd/error_log - ошибки конкретного сайта могут быть в отдельном файле из
ErrorLogвнутри VirtualHost.
sudo tail -n 200 /var/log/apache2/error.log
sudo tail -n 200 /var/log/httpd/error_log
Типовые строки, которые прямо указывают на причину:
client denied by server configurationилиAH01630: client denied by server configuration— запрет на уровнеRequire/<Directory>/.htaccess;AH00036: access to ... denied (filesystem path ...)— правила<Directory>не покрывают реальный путь или конфликтуют;Permission deniedпри попытке открыть файл/каталог — чаще всего права на ФС или SELinux;- упоминания
suexec— отказ запуска CGI/скрипта из-за требований suEXEC.
Шаг 2. Apache 2.4: Require/Directory и «Require all granted»
В Apache 2.4 доступ управляется директивой Require. Самый частый сценарий после переноса конфигов (или при сборке нового vhost): админ ожидает «по умолчанию разрешено», а фактически нет явного разрешающего правила на нужный каталог.
Если в error log есть «client denied by server configuration», почти всегда причина в блоках
<Directory>или.htaccess: отсутствуетRequire all granted, либо выше по дереву есть более строгий запрет, который выигрывает по приоритету.
Разрешение доступа к DocumentRoot: проверьте совпадение путей
Apache принимает решение на основании наиболее подходящих блоков <Directory> (и .htaccess, если разрешено через AllowOverride). Типичная ошибка: vhost указывает один DocumentRoot, а правила <Directory> описывают другой путь или не описывают вовсе.
Убедитесь, что для реального DocumentRoot есть явный блок с разрешением:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example/public
<Directory /var/www/example/public>
Require all granted
Options FollowSymLinks
AllowOverride All
</Directory>
</VirtualHost>
Ключевой момент: Require all granted должен применяться к тому каталогу, из которого реально отдаются файлы.
Частые ловушки в Directory/Require
- Запрет стоит выше: общий блок для
/var/wwwсRequire all deniedперекрывает частный (или подключается конфиг с более высоким приоритетом). - Неправильный контейнер:
<Location>работает с URL, а<Directory>— с путями ФС. ДляDocumentRootпочти всегда нужен<Directory>. - Симлинки: при раздаче через symlink запрет может прилететь из
Options -FollowSymLinksили требованияSymLinksIfOwnerMatch. - Нет «x» на родительских каталогах: даже при
Require all grantedApache не «пройдет» по пути без execute на директориях (см. следующий шаг).

Шаг 3. Права на файлы и каталоги: диагностика по всей цепочке пути
Когда Apache пишет «Permission denied», это не всегда про Require. Очень часто forbidden apache связан с DAC-правами на уровне ОС: неверный владелец, слишком строгий режим, или отсутствует execute-бит на одном из каталогов по пути.
Лучшая команда для 403 из-за прав
namei -l /var/www/example/public/index.php
namei -l показывает права на каждый каталог в цепочке. Ищите место, где пользователь/группа Apache «упирается» (обычно нет «x»).
Отправная точка, которая обычно работает для статики:
- каталоги:
755(всем «x», чтобы можно было «проходить» по пути); - файлы:
644(читать можно, писать нет).
Это не универсальное правило, но полезный базовый ориентир для диагностики.
Под каким пользователем работает Apache
ps -eo user,group,comm | grep -E 'apache2|httpd' | head
Также можно посмотреть директивы User и Group в конфиге Apache (расположение зависит от дистрибутива).
Почему chmod -R 777 не лечит 403
Попытка «вылечить» 403 правами 777 обычно делает только хуже:
- резко увеличивает поверхность атаки (любой локальный пользователь/процесс может писать в код/конфиги);
- не помогает при SELinux (MAC важнее DAC);
- может ломать ограничения suEXEC, где критичны владельцы и «неопасные» режимы.
Правильный подход: обеспечить минимально достаточные права на проход по каталогам и чтение целевых файлов, а запись давать только там, где она реально нужна.
Шаг 4. DocumentRoot, Alias и «не тот каталог»: когда конфиг выглядит правильно
Иногда вы правите «правильный» DocumentRoot, но запрос фактически уходит в другой путь из-за Alias, ScriptAlias, дополнительного vhost или включенного набора конфигов. В итоге правило/права исправлены «не там», и 403 остается.
Что проверить в первую очередь:
- есть ли в конфиге
Alias/ScriptAliasдля нужного URI; - не подключен ли другой конфиг в
conf.d/sites-enabled, который переопределяет доступ; - не перепутан ли каталог публикации (
public,www,htdocs).
Самая полезная команда при множестве include — «карта» активной конфигурации:
apachectl -S
Она показывает, какой vhost обслуживает запрос и из какого файла он загружен. После этого обычно становится ясно, почему «вроде все настроено, но не работает».
Если вы параллельно приводите в порядок заголовки безопасности и хотите понять, какие заголовки реально отдаются на ответах, пригодится отдельный разбор: как настроить HTTP security headers в Nginx и Apache.
Шаг 5. suEXEC: 403 из-за владельцев и «небезопасных» прав
Если включен suexec (или схожий механизм), 403 может быть не про Require, а про то, что Apache отказывается запускать CGI/скрипты из-за несоответствия требованиям: владелец, права, расположение каталога вне разрешенного дерева и т.д.
Как понять, что виноват suEXEC
Частые признаки:
- 403 возникает только на CGI/скрипте, а статика отдается;
- в error log появляются строки с
suexecили отказ запуска; - на RHEL-подобных системах может быть отдельный лог suEXEC.
Проверьте модуль:
apachectl -M | grep -i suexec
И посмотрите ограничения suEXEC (важны DOC_ROOT и прочие параметры):
sudo suexec -V
Если ваш DocumentRoot находится вне DOC_ROOT, suEXEC может законно отказать.
Частые причины отказа suEXEC
- скрипт/каталог имеет «опасные» права (например, доступ на запись для группы/всех там, где suEXEC ожидает жестче);
- владелец файла/каталога не совпадает с ожидаемым;
- скрипт запускается из каталога вне разрешенного дерева.
Шаг 6. SELinux: права в Linux правильные, но Apache все равно возвращает 403
На RHEL/Alma/Rocky/CentOS при включенном SELinux в режиме Enforcing одних UNIX-прав может быть недостаточно. Процессу Apache может быть запрещено читать файлы из-за контекста безопасности. Снаружи это выглядит как «обычный 403» или как Permission denied в логах.
Быстрая проверка режима SELinux
getenforce
sestatus
Если видите Enforcing, SELinux участвует в решении.
Смотрим, что именно блокируется
Самый полезный источник — audit log с AVC событиями:
sudo tail -n 200 /var/log/audit/audit.log
Если установлены утилиты, удобно быстро отфильтровать по httpd:
sudo ausearch -m avc -ts recent | grep -i httpd | tail -n 50
Исправление: правильный контекст на DocumentRoot
Для статической отдачи контент обычно должен иметь тип вроде httpd_sys_content_t. Посмотрите текущие контексты:
ls -laZ /var/www/example/public | head
Если контекст «не вебовый» (часто после rsync из домашней директории или деплоя в нестандартный путь), Apache будет получать запрет.
Типовой подход: назначить правильный контекст и закрепить его правилом:
sudo semanage fcontext -a -t httpd_sys_content_t '/var/www/example/public(/.*)?'
sudo restorecon -Rv /var/www/example/public
Если приложению нужно писать (uploads, cache), делайте это в отдельном каталоге и назначайте права и контексты именно для него. Не превращайте весь проект в «writeable» — это обычно и небезопасно, и сложно поддерживать.

Шаг 7. Быстрый чек-лист: диагностика за 10 минут
- Откройте error log и найдите точную строку отказа (особенно «client denied by server configuration»).
- Проверьте, какой vhost обслуживает запрос:
apachectl -S. - Убедитесь, что для
DocumentRootесть<Directory ...>сRequire all granted(Apache 2.4). - Проверьте права по всей цепочке:
namei -l /path/to/file. - На RHEL-подобных:
getenforce, затем audit.log на AVC и при необходимостиrestoreconиsemanage fcontext. - Если 403 на CGI: проверьте
suexec -Vи сообщения suEXEC в логах.
Типовые сценарии и решения
Сценарий A: «Включил новый vhost, вижу 403 на /»
Чаще всего отсутствует разрешение в <Directory> для нового DocumentRoot. Добавьте блок, проверьте конфигурацию и перезагрузите Apache.
sudo apachectl configtest
sudo systemctl reload apache2
sudo systemctl reload httpd
Сценарий B: «Все права 755/644, но все равно 403 на Alma/Rocky»
С высокой вероятностью это SELinux и неверный контекст на файлах. Проверьте ls -Z, примените restorecon, а для нестандартного пути добавьте правило semanage fcontext.
Сценарий C: «403 только на uploads или cache»
Это уже не чтение, а запись. Нужны корректные права на запись и корректный SELinux-контекст для каталога записи. Не делайте весь проект записываемым — вынесите запись в отдельные директории.
Сценарий D: «403 при раздаче через симлинк»
Проверьте:
- включен ли
Options FollowSymLinks(или соблюдены условияSymLinksIfOwnerMatch) в нужном<Directory>; - права и владельцев целевого каталога симлинка;
- SELinux-контекст целевого пути (если применимо).
Как меньше ловить 403 при деплое: практики из админки
- Единый стандарт путей: держите сайты в предсказуемых каталогах, чтобы реже упираться в suEXEC/SELinux.
- Разделяйте код и запись: код и статика должны быть read-only, запись — только в выделенные каталоги (uploads/cache/tmp).
- Проверяйте конфиг перед reload:
apachectl configtestв CI или в деплой-скрипте. - После миграций на RHEL-подобных: не забывайте про
restoreconна дерево сайта.
Если вы выбираете, на чем размещать проекты и как проще разруливать права/владельцев на нескольких сайтах, удобно разводить их по отдельным инстансам на VDS: меньше сюрпризов с окружением и проще диагностика 403 по «чистым» логам.
Вывод
403 Forbidden в Apache — это не «мистика», а конкретный запрет: либо правила доступа (Require/<Directory>), либо права ФС, либо ограничения suexec, либо SELinux для httpd. Самый быстрый путь к решению: начать с error log, затем проверить, какой vhost обслуживает запрос, и пройтись по цепочке доступа от конфигурации до контекста SELinux.
Когда диагностика выстроена по шагам, forbidden apache перестает быть черным ящиком и превращается в рутинную задачу.


