Как диагностировать ошибку 403 Forbidden
27 Oct 2026, 16:38:28
HTTP 403 Forbidden означает, что сервер получил и понял запрос, но отказывается его выполнять. В отличие от 404, ресурс может существовать, но доступ к нему запрещён. Отказ может исходить от разных уровней, и задача диагностики в том, чтобы найти, какой из них сработал:Клиент → CDN/прокси → WAF → веб-сервер (Nginx/Apache) → файловая система → приложение (PHP, CMS)
Менять права или отключать защиту «наугад» не стоит: сначала нужно установить причину.
1. Проверьте HTTP-ответ сервера
Убедитесь, что 403 возвращается стабильно и откуда именно.curl -I https://example.com/Пример ответа:
HTTP/2 403
server: nginx
date: Tue, 27 Oct 2026 11:15:42 GMT
content-type: text/html
content-length: 153
Для подробного вывода (TLS, заголовки запроса и ответа):
curl -v https://example.com/Сравните разные адреса, чтобы понять масштаб проблемы:
curl -sS -o /dev/null -w '%{http_code}\n' https\://example.com/ # главная
curl -sS -o /dev/null -w '%{http_code}\n' https\://example.com/admin/ # каталог
curl -sS -o /dev/null -w '%{http_code}\n' https\://example.com/style.css # статический файл
curl -sS -o /dev/null -w '%{http_code}\n' https\://example.com/index.php # PHP-файлКак читать результат:
Результат | Куда смотреть |
| 403 везде | Права на корневой каталог, конфигурация виртуального хоста, WAF/CDN |
| 403 только на /admin/ | Блок location, .htaccess, ограничения приложения |
| 403 только на .php | Настройки PHP-обработчика, WAF, правила приложения |
| 403 на каталоге, но файл внутри открывается | Нет индексного файла, запрещён листинг каталога |
2. Проверьте логи веб-сервера
Это самый эффективный шаг. Лог часто прямо называет причину.Nginx
tail -n 100 /var/log/nginx/error.logЧтобы видеть записи в момент запроса, откройте два терминала. В первом:
tail -f /var/log/nginx/error.logВо втором выполните `curl -I https://example.com/admin/` (или откройте страницу в браузере) и посмотрите, что появилось в первом.
Примеры сообщений и их смысл:
Нет индексного файла, листинг каталога запрещён:
2026/10/27 12:15:42 [error] 1234#1234: *5 directory index of "/var/www/example.com/public/uploads/" is forbidden, client: 203.0.113.5, server: example.com, request: "GET /uploads/ HTTP/2.0", host: "example.com"В каталоге uploads нет index.html/index.php, а autoindex выключен. Это нормальное поведение, а не поломка.
Нет прав на файл или на каталог в пути к нему:
2026/10/27 12:17:03 [error] 1234#1234: *7 open() "/var/www/example.com/private/file.php" failed (13: Permission denied), client: 203.0.113.5, server: example.com, request: "GET /private/file.php HTTP/2.0", host: "example.com"Код 13: Permission denied означает, что у пользователя, от которого работает Nginx (обычно www-data), нет доступа к файлу или к одному из каталогов выше.
Если лог пуст, возможно, он находится в другом месте:
nginx -T 2>&1 | grep -E 'error_log|access_log'Пример вывода:
access_log /var/log/nginx/example.com.access.log;То есть у этого сайта собственные журналы, и смотреть нужно их.
error_log /var/log/nginx/example.com.error.log warn;
Apache
tail -n 100 /var/log/apache2/error.log
tail -f /var/log/apache2/error.logПримеры сообщений:
[Tue Oct 27 12:20:11.123456 2026] [authz_core:error] [pid 2345] [client 203.0.113.5:51234] AH01630: client denied by server configuration: /var/www/example.com/public/admin/Доступ запрещён директивами конфигурации (Require all denied, Require ip ...) или правилами в .htaccess.
[core:error] [pid 2345] [client 203.0.113.5:51240] AH01276: Cannot serve directory /var/www/example.com/public/uploads/: No matching DirectoryIndex (index.html,index.php) found, and server-generated directory index forbidden by Options directiveАналог directory index ... is forbidden в Nginx: нет индексного файла, а Options -Indexes запрещает листинг.
[core:error] [pid 2345] [client 203.0.113.5:51250] AH00035: access to /index.html denied (filesystem path '/var/www/example.com') because search permissions are missing on a component of the pathНет права x на один из каталогов в пути. Это классическая проблема с правами.
Если Nginx стоит перед Apache в роли прокси, смотрите логи обоих серверов: 403 мог вернуть любой из них.
Пути к логам зависят от ОС, конфигурации и панели управления (в CentOS/RHEL, например, Apache пишет в /var/log/httpd/error_log).
3. Проверьте права доступа к файлам и каталогам
Предположим, сайт лежит в /var/www/example.com/public/.ls -ld /var/www/example.com
ls -ld /var/www/example.com/public
ls -l /var/www/example.com/public/Для доступа к файлу процессу нужно право на чтение самого файла и право выполнения (x) на каждом каталоге в пути. Самый удобный способ увидеть всю цепочку:
namei -l /var/www/example.com/public/index.htmlПример проблемного вывода:
f: /var/www/example.com/public/index.htmlКаталог example.com имеет права 700, и владелец у него deploy. Пользователь www-data не может пройти через него, хотя внутри public права «правильные». Nginx напишет Permission denied.
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwx------ deploy deploy example.com <-- здесь проблема
drwxr-xr-x deploy deploy public
-rw-r--r-- deploy deploy index.html
Узнайте, от какого пользователя работает веб-сервер:
ps aux | grep -E 'nginx|apache2|httpd' | grep -v greproot 812 ... nginx: master process /usr/sbin/nginxРабочие процессы запущены от www-data. В Nginx пользователя задаёт директива user в nginx.conf.
www-data 813 ... nginx: worker process
Варианты исправления (выбирайте осознанно, а не механически):
Вариант 1: разрешить всем проход (x) через каталог
chmod 755 /var/www/example.comВариант 2: безопаснее, если веб-серверу достаточно группового доступа
chgrp www-data /var/www/example.com
chmod 750 /var/www/example.comКакие права обычно используются:- каталоги: 755;
- обычные файлы: 644.
Не делайте так:
chmod -R 777 /var/www/example.com # небезопасно и часто не решает проблему4. Проверьте наличие индексного файла
Если 403 возникает при открытии каталога или главной страницы:ls -la /var/www/example.com/public/Пример проблемной ситуации:
drwxr-xr-x 3 deploy deploy 4096 Oct 27 12:00 .Файла index.html или index.php нет, есть только home.html. Сервер ищет индексный файл по списку, не находит его и, раз листинг запрещён, отвечает 403.
drwxr-xr-x 4 deploy deploy 4096 Oct 27 11:50 ..
-rw-r--r-- 1 deploy deploy 120 Oct 27 12:00 home.html
drwxr-xr-x 2 deploy deploy 4096 Oct 27 11:55 assets
Nginx
server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
index index.php index.html;
}Если вы хотите, чтобы главной страницей был home.html, добавьте его в index:
index home.html index.php index.html;Для PHP-сайта проверьте также передачу запросов в PHP-FPM:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}Apache
DirectoryIndex index.php index.html
Options -Indexes
Require all granted
Обратите внимание на Require all granted: если этой директивы нет (а в основной конфигурации по умолчанию доступ к каталогу запрещён), Apache вернёт 403 даже при идеальных правах на файлы.
Не добавляйте индексные файлы вслепую: сначала убедитесь, что приложение действительно использует именно этот файл. Если нужно показать содержимое каталога намеренно, это включается отдельно (autoindex on; в Nginx или Options +Indexes в Apache). Делайте это только если понимаете последствия для безопасности.
5. Проверьте конфигурацию веб-сервера
NginxНайдите директивы, которые явно запрещают доступ:
location /admin/ {
allow 192.0.2.10;
deny all;
}location /private/ {
return 403;
}В первом примере в /admin/ пустят только адрес 192.0.2.10, всех остальных ждёт 403. Правила allow/deny проверяются по порядку, и срабатывает первое подходящее.
Частая ошибка:
location /admin/ {
deny all;
allow 192.0.2.10; # недостижимо: deny all сработает раньше
}Посмотреть итоговую конфигурацию со всеми подключёнными файлами:
nginx -T
nginx -T > /tmp/nginx-config.txt 2>&1
grep -n -E 'server_name|deny|allow|return 403' /tmp/nginx-config.txtПеред перезагрузкой всегда проверяйте синтаксис:
nginx -t && systemctl reload nginxApache
Require ip 192.0.2.10Доступ разрешён только с одного адреса. Запрет для всех:
Require all deniedНайдите все '.htaccess' на сайте:
find /var/www/example.com -name .htaccess -type fИ выведите из них директивы доступа:
grep -rn -E 'Require|Deny|Allow|Order|RewriteRule.\*\[F' /var/www/example.com --include=.htaccessВ .htaccess часто встречается смесь старого (Apache 2.2) и нового ( Apache 2.4) синтаксиса:
Старый синтаксис; в Apache 2.4 работает только при включённом mod_access_compat
Order deny,allow
Deny from allСовременный синтаксис Apache 2.4
Require all deniedФлаг [F] в RewriteRule тоже возвращает 403:
RewriteRule ^secret/ - [F,L]Проверить синтаксис конфигурации Apache:
apachectl configtest6. Проверьте WAF и защитные механизмы
Если права и конфигурация в порядке, а в логах веб-сервера нет объяснения, 403 может возвращать защитный слой: ModSecurity, WAF хостинга, плагин CMS (например, Wordfence), fail2ban, CDN.ModSecurity
Типичная ситуация: форма или URL содержит «подозрительные» символы, например SQL-подобный текст или в параметре. Запрос блокируется, хотя ничего вредоносного в нём нет.Пример записи в логе (формат зависит от версии):
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator ' Ge' with parameter '5' against variable ' TX:ANOMALY_SCORE' (Value: ' 10') [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] [uri "/search"] [unique_id "ZxYz..."]
Важные поля: id (идентификатор сработавшего правила), msg (описание), uri (какой адрес заблокирован). Найдите записи по времени запроса:
grep -i modsecurity /var/log/apache2/error.log | tail -n 20или, если настроен отдельный журнал аудита:
tail -n 100 /var/log/modsec_audit.logИдентификатор 949110 относится к общему порогу аномальности в OWASP CRS: он показывает, что набралось слишком много баллов от других правил. Искать причину нужно среди соседних записей с теми же unique_id.
Если правило срабатывает ложно, обычно его исключают точечно (для конкретного id и конкретного адреса), а не отключают ModSecurity целиком.
Cloudflare и другие CDN/прокси
Сначала выясните, кто возвращает 403. Посмотрите заголовки:curl -I https://example.com/HTTP/2 403
server: cloudflare
cf-ray: 8a1b2c3d4e5f6789-AMS
'server: cloudflare' и заголовок 'cf-ray' показывают, что ответ дала система Cloudflare, а не ваш сервер. В теле страницы часто есть код вроде 'Error 1020 Access denied', который указывает на сработавшее правило файрвола.
Теперь проверьте сервер-источник в обход CDN, подставив его IP (ORIGIN_IP — адрес вашего сервера):
curl -I --resolve example.com:443:ORIGIN_IP https\://example.com/- Если напрямую приходит 200, а через CDN 403, проблема в правилах CDN (WAF, Bot Fight Mode, блокировка страны или IP).
- Если и напрямую приходит 403, причина на вашем сервере.
7. Проверьте ограничения по IP-адресу
Типичный симптом: с вашего компьютера сайт открывается, а у клиента или с сервера отдаёт 403 (или наоборот).Пример сравнения. С локального компьютера:
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' https\://example.com/HTTP 200С сервера или другого подключения (например, мобильный интернет):
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' https\://example.com/ HTTP 403Теперь определите, с какого адреса вы выходите в сеть, и поищите его в конфигурации и логах:
curl -s https://ifconfig.me
grep 203.0.113.5 /var/log/nginx/access.log | tail
grep -rn 203.0.113.5 /etc/nginx/ /etc/apache2/Если IP фигурирует в deny, в списке fail2ban или в правилах WAF, причина найдена. Проверить fail2ban:
fail2ban-client status
fail2ban-client status sshd # имя jail может быть другим (nginx-http-auth, apache-badbots и т. д.)Различие в ответах помогает сузить поиск, но не доказывает, что причина именно в IP. На разницу влияют DNS, прокси, маршруты, User-Agent и заголовки. Поэтому проверьте и это:
curl -I -A "Mozilla/5.0 (X11; Linux x86_64)" https\://example.com/Если с «браузерным» User-Agent приходит 200, а с curl/8.x приходит 403, запрос блокирует правило по User-Agent (в WAF, .htaccess или защитном плагине).
8. Проверьте журналы PHP и приложения
Если 403 появляется только на отдельных страницах, в админке или при отправке формы, причина часто в самом приложении.Примеры, когда приложение осознанно возвращает 403:
- Django: «CSRF verification failed. Request aborted» (не пройдена проверка CSRF-токена);
- WordPress: плагин безопасности блокирует запрос к wp-admin или admin-ajax.php;
- приложение проверяет роль пользователя и отказывает без нужных прав;
- ограничение по количеству запросов (rate limit) реализовано на уровне приложения.
Служба PHP-FPM (имя зависит от версии)
journalctl -u php8.2-fpm --since "30 minutes ago"Журнал ошибок PHP, если путь известен
php -i | grep -i error_log
tail -n 100 /var/log/php8.2-fpm.logWordPress: включите логирование в wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);затем смотрите wp-content/debug.log
tail -n 50 /var/www/example.com/public/wp-content/debug.logУчитывайте, что ошибки PHP сами по себе обычно дают 500, а не 403. Если в логах PHP чисто, это не исключает блокировку на уровне веб-сервера или WAF.
9. Сопоставьте время запроса с логами
Если причина не очевидна, сделайте один контрольный запрос и сразу найдите его в логах.date
curl -sS -o /dev/null -w 'HTTP status: %{http_code}\n' https\://example.com/admin/
tail -n 50 /var/log/nginx/error.logЕсли в error.log ничего нет, посмотрите журнал доступа:
tail -n 100 /var/log/nginx/access.logПример записи со стандартным форматом:
203.0.113.5 - - [27/Oct/2026:12:15:42 +0100] "GET /admin/ HTTP/2.0" 403 153 "-" "curl/8.5.0"Здесь видно клиентский IP, время, метод и адрес, код ответа 403 и User-Agent. Быстро найти все 403:
awk '$9 == 403' /var/log/nginx/access.log | tail -n 20(Номер поля '9' верен для стандартного формата 'combined'; при изменённом формате логов он будет другим.)Для периодической ошибки сопоставляйте время нескольких запросов сразу с тремя источниками: логом веб-сервера, журналом WAF и логами приложения.
10. Краткий алгоритм диагностики
- Проверьте код ответа: curl -I https://example.com/.
- Определите, кто отвечает: заголовки server, cf-ray, тело страницы. Это ваш сервер, прокси или CDN?
- Повторите запрос и посмотрите error.log веб-сервера.
- Если в логе Permission denied, выполните namei -l по пути к файлу и проверьте пользователя веб-сервера.
- Если запрашивается каталог, проверьте индексный файл и директивы index / DirectoryIndex, а также Require all granted в Apache.
- Если в логе «denied by server configuration», изучите deny/allow/return 403 в Nginx или Require/Deny и .htaccess в Apache.
- Если в логах веб-сервера объяснения нет, проверьте WAF/ModSecurity, CDN и защитные плагины CMS.
- Если результат зависит от подключения, сравните ответы с разных IP и с разными User-Agent.
- Проверьте логи приложения: 403 мог вернуть сам код сайта.
- Внесите одно изменение, повторите запрос и убедитесь, что ошибка устранена. Не меняйте несколько параметров сразу, иначе непонятно, что именно помогло.
Заключение
HTTP 403 может иметь самые разные причины: от неправильных прав на каталог до сработавшего правила WAF. Поэтому не стоит сразу ставить chmod 777 или отключать защиту.Начинайте с логов веб-сервера в момент ошибки: сообщения вроде Permission denied, directory index ... is forbidden или client denied by server configuration почти всегда сразу показывают, где искать. Если логи молчат, последовательно проверяйте конфигурацию доступа, WAF и CDN, ограничения по IP и логи приложения.