Как диагностировать ошибку 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 на каталоге, но файл внутри открываетсяНет индексного файла, запрещён листинг каталога
Важно: заголовок server: nginx или server: Apache лишь показывает, какой сервер отвечает, но не объясняет причину отказа.

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
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
Каталог example.com имеет права 700, и владелец у него deploy. Пользователь www-data не может пройти через него, хотя внутри public права «правильные». Nginx напишет Permission denied.
Узнайте, от какого пользователя работает веб-сервер:
ps aux | grep -E 'nginx|apache2|httpd' | grep -v grep
root     812  ... nginx: master process /usr/sbin/nginx
www-data 813  ... nginx: worker process
Рабочие процессы запущены от www-data. В Nginx пользователя задаёт директива user в nginx.conf.
Варианты исправления (выбирайте осознанно, а не механически):
 Вариант 1: разрешить всем проход (x) через каталог
chmod 755 /var/www/example.com
 Вариант 2: безопаснее, если веб-серверу достаточно группового доступа
chgrp www-data /var/www/example.com
chmod 750 /var/www/example.com
Какие права обычно используются:
  • каталоги: 755;
  • обычные файлы: 644.
Это не универсальное правило. Многое зависит от владельца файлов, пользователя веб-сервера, PHP-FPM и требований приложения. Например, каталог загрузок WordPress должен быть доступен на запись пользователю, от которого работает PHP.
Не делайте так:
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 .
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
Файла index.html или index.php нет, есть только home.html. Сервер ищет индексный файл по списку, не находит его и, раз листинг запрещён, отвечает 403.
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 nginx
Apache
    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 configtest

6. Проверьте 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, причина на вашем сервере.
Не отключайте защиту целиком. Найдите конкретное правило, которое сработало (в панели CDN это журнал событий безопасности), и скорректируйте именно его.

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.log

WordPress: включите логирование в 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. Краткий алгоритм диагностики

  1. Проверьте код ответа: curl -I https://example.com/.
  2. Определите, кто отвечает: заголовки server, cf-ray, тело страницы. Это ваш сервер, прокси или CDN?
  3. Повторите запрос и посмотрите error.log веб-сервера.
  4. Если в логе Permission denied, выполните namei -l по пути к файлу и проверьте пользователя веб-сервера.
  5. Если запрашивается каталог, проверьте индексный файл и директивы index / DirectoryIndex, а также Require all granted в Apache.
  6. Если в логе «denied by server configuration», изучите deny/allow/return 403 в Nginx или Require/Deny и .htaccess в Apache.
  7. Если в логах веб-сервера объяснения нет, проверьте WAF/ModSecurity, CDN и защитные плагины CMS.
  8. Если результат зависит от подключения, сравните ответы с разных IP и с разными User-Agent.
  9. Проверьте логи приложения: 403 мог вернуть сам код сайта.
  10. Внесите одно изменение, повторите запрос и убедитесь, что ошибка устранена. Не меняйте несколько параметров сразу, иначе непонятно, что именно помогло.

Заключение 

HTTP 403 может иметь самые разные причины: от неправильных прав на каталог до сработавшего правила WAF. Поэтому не стоит сразу ставить chmod 777 или отключать защиту.
Начинайте с логов веб-сервера в момент ошибки: сообщения вроде Permission denied, directory index ... is forbidden или client denied by server configuration почти всегда сразу показывают, где искать. Если логи молчат, последовательно проверяйте конфигурацию доступа, WAF и CDN, ограничения по IP и логи приложения.