Безопасность сайта через .htaccess

Файл .htaccess — первая линия обороны сайта на Apache: им закрывают доступ к чувствительным файлам, ограничивают вход по IP или паролю, отключают листинг каталогов, запрещают выполнение PHP в папках загрузок, отбивают ботов и хотлинк, ставят заголовки безопасности. Ниже — готовые блоки под каждую задачу: вставляйте в .htaccess в корне сайта (или в нужном каталоге), плейсхолдеры (example.com, 203.0.113.x, пути) замените на свои. При сомнениях прогоните файл через линтер /check/, правила mod_rewrite — через тестер /rewrite/, а собрать .htaccess из чекбоксов можно в генераторе. В конце — чеклист на 5 минут.

Важно про синтаксис: блоки ниже даны для Apache 2.4 (Require all denied, Require ip). Если у вас Apache 2.2 — нужен старый синтаксис (Order / Allow / Deny); сконвертировать туда-обратно поможет конвертер 2.2↔2.4.

1. Заголовки безопасности (одной строкой)

HTTP-заголовки ответа защищают посетителей от кликджекинга, части XSS, downgrade-атак и «угадывания» MIME-типа. Ставятся через mod_headers директивой Header always set (always — чтобы заголовок добавлялся и к страницам ошибок 403/404).

.htaccessкопировать
<IfModule mod_headers.c>
    # HSTS — только если сайт уже полностью на HTTPS
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    # начните с Content-Security-Policy-Report-Only, потом ужесточайте
    Header always set Content-Security-Policy "default-src 'self'"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>
  • HSTS (Strict-Transport-Security) запрещает открыть сайт по http:// в течение max-age — включайте осознанно, начните с маленького значения (например, max-age=300).
  • CSP легко ломает inline-скрипты и стили — сначала включите Content-Security-Policy-Report-Only, посмотрите отчёты, потом переключайте на боевой заголовок.
  • Без always заголовок не попадёт на ответы с ошибкой (403 / 404 / 500).

Подробный разбор каждого заголовка и его опций — справочник HTTP-заголовков; собрать нужные чекбоксами — генератор (блок «Security headers»).

2. Закрыть доступ к чувствительным файлам

По умолчанию веб-сервер отдаёт любой файл в корне сайта — включая конфиги с паролями БД, дампы, бэкапы, историю git. Закройте их явно.

.htaccessкопировать
<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>
.htaccessкопировать
<FilesMatch "(^\.env|^wp-config\.php$|^configuration\.php$|^composer\.(json|lock)$|\.(bak|old|orig|save|swp|sql|sqlite|log)$|^readme\.html$)">
    Require all denied
</FilesMatch>
.htaccessкопировать
RedirectMatch 404 /\.(git|svn|hg)(/|$)

Синтаксис выше — Apache 2.4 (Require all denied). На Apache 2.2 внутри <FilesMatch> нужно Order allow,deny + Deny from all — сконвертировать поможет конвертер 2.2↔2.4. Проверить, что блок без ошибок, — линтер .htaccess.

3. Отключить листинг каталогов

Если в папке нет index.php / index.html, Apache (mod_autoindex) показывает список её файлов — видно структуру, забытые бэкапы и дампы. Отключите автоиндекс:

.htaccessкопировать
Options -Indexes

Если листинг где-то всё же нужен — спрячьте опасные расширения: IndexIgnore *.bak *.sql *.zip .*. Или просто положите в каталог пустой index.html.

  • Грабли: Options -Indexes может требовать AllowOverride Options (или AllowOverride All) — иначе 500 Internal Server Error с .htaccess: Options not allowed here; тогда правило ставит хостер в конфиге сервера.

Что делать, если .htaccess уже отдаёт 500, — гид «Почему .htaccess не работает».

4. Доступ только с доверенных IP

Типовой кейс: закрыть /wp-admin/, staging-копию, /phpmyadmin/ или служебные скрипты — только со своего или офисного IP. Положите такой .htaccess в нужный каталог:

.htaccessкопировать
<RequireAll>
    Require ip 203.0.113.0/24
    Require ip 192.0.2.15
</RequireAll>

Если условие одно — достаточно просто Require ip 203.0.113.0/24 192.0.2.15 без <RequireAll>. Наоборот — пускать всех, кроме нескольких IP:

.htaccessкопировать
<RequireAll>
    Require all granted
    Require not ip 198.51.100.7
</RequireAll>
  • За CDN / прокси %{REMOTE_ADDR} — это IP прокси, а не клиента; реальный IP передаётся в заголовке X-Forwarded-For (его разбор — /headers/ → заголовки запроса), и фильтровать по нему в .htaccess ненадёжно.

Apache 2.2-синтаксис (Order deny,allow / Allow from) — конвертер 2.2↔2.4. Подробный гайд — статья «Ограничение доступа по IP». Заблокировать целые страны (по GeoIP / Cloudflare / спискам IP) — генератор /geo-block/.

5. Защита паролем (HTTP Basic)

Быстрый способ закрыть staging или служебный раздел — HTTP Basic-авторизация. Подходит «для своих»; не подходит как серьёзная защита: сам по себе Basic не шифрует данные (нужен HTTPS), нет кнопки «выйти», пароль шлётся при каждом запросе.

.htaccessкопировать
AuthType Basic
AuthName "Restricted area"
AuthUserFile /home/USER/.htpasswd
Require valid-user
  • AuthUserFileабсолютный путь; файл .htpasswd кладите вне document root, чтобы его нельзя было скачать по прямой ссылке.
  • Пароли в .htpasswd храните хешированными (bcrypt или APR1-MD5), не открытым текстом.

Скомбинировать «или из офиса, или по паролю» (Apache 2.4):

.htaccessкопировать
AuthType Basic
AuthName "Restricted"
AuthUserFile /home/USER/.htpasswd
<RequireAny>
    Require ip 203.0.113.0/24
    Require valid-user
</RequireAny>

Сгенерировать .htpasswd (bcrypt / APR1-MD5 / SHA / crypt) — генератор .htpasswd; пошаговый туториал — статья «Защита папки паролем».

6. Запретить выполнение PHP в загрузках

Главный вектор взлома CMS: через форму загрузки заливают shell.php в /uploads/ и запускают. Контрмера — в .htaccess внутри папки загрузок запретить отдачу и выполнение PHP-файлов:

.htaccessкопировать
<FilesMatch "\.(ph(p[0-9]?|tml|ar|tt)|phps)$">
    Require all denied
</FilesMatch>
# на старых конфигурациях вместо/вместе:
#   php_flag engine off
#   RemoveHandler .php .phtml
#   RemoveType .php
  • Грабли: злоумышленник может залить в /uploads/ и собственный .htaccess (включив выполнение обратно), если AllowOverride в конфиге сервера это позволяет. Надёжная контрмера ставится хостером: AllowOverride None (или хотя бы без FileInfo) на каталоге загрузок — из .htaccess себя так не «выключить».

Проверить, что правило написано без ошибок, — линтер .htaccess.

7. Блокировка ботов, сканеров и хотлинка

Плохие краулеры и сканеры уязвимостей жрут трафик и ищут дыры; хотлинк ворует трафик с картинок. Блокировка по User-Agent (через mod_setenvif — безопаснее, чем [OR]-цепочки в mod_rewrite):

.htaccessкопировать
SetEnvIfNoCase User-Agent "(libwww|wget|nikto|sqlmap|nmap|masscan|zgrab|python-requests)" bad_bot
<RequireAll>
    Require all granted
    Require not env bad_bot
</RequireAll>

Анти-хотлинк — отдавать 403 на чужие реферы для картинок (замените example\.com на свой домен):

.htaccessкопировать
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC]
RewriteRule \.(jpe?g|png|gif|webp|svg)$ - [F]
  • Не блокируйте по слишком общим словам (bot, http) — случайно отрубите Googlebot, Bingbot или превью соцсетей (Facebook, Telegram, Twitter).
  • User-Agent и Referer тривиально подделываются — это фильтр от ленивых, не защита.
  • Пустой Referer (!^$) бывает у легитимных переходов из закладок и при HTTPS → HTTP — учитывайте.

Собрать списки ботов и доменов чекбоксами — генератор (блоки «Блокировка ботов», «Защита от хотлинка»); проверить правило на конкретном URL — RewriteRule-тестер; про блокировку по реферу подробно — статья. А если у вас уже есть access.log — прогоните его через анализатор access.log — он найдёт реальных нарушителей и предложит правила; полный гид по ботам, скраперам и AI-краулерам — хаб «Блокировка ботов».

8. Hardening PHP и веб-сервера

Уберите утечки версий и лишние возможности. Скрыть «визитку» PHP / фреймворка из заголовков ответа:

.htaccessкопировать
<IfModule mod_headers.c>
    Header always unset X-Powered-By
    Header always unset X-AspNet-Version
</IfModule>

PHP-настройки — только если PHP подключён как модуль Apache (mod_php) и только те, что уровня PHP_INI_ALL / PHP_INI_PERDIR:

.htaccessкопировать
php_flag display_errors Off
php_flag log_errors On
php_value upload_max_filesize 8M
LimitRequestBody 10485760
  • Большие грабли: на PHP-FPM / FastCGI / CGI (это большинство современных хостингов) директивы php_value / php_flag в .htaccess дают 500 Internal Server Error (Invalid command 'php_flag'). Там настройки PHP правят через .user.ini в корне сайта или панель хостинга. Линтер /check/ ловит php_value / php_flag и предупреждает.
  • Настройки уровня PHP_INI_SYSTEM (expose_php, allow_url_include, allow_url_fopen) из .htaccess менять нельзя в принципе — только в php.ini.
  • ServerSignature Off / ServerTokens Prod (убрать версию Apache / ОС из ответов и страниц ошибок) в .htaccess не работают — только в конфиге сервера или виртхоста; упомянуто, чтобы вы не искали зря. LimitRequestBody — наоборот, core-директива Apache, работает на любой конфигурации.

9. Чеклист: проверь за 5 минут

  1. Заголовки безопасности стоят (HSTS, CSP, X-Frame-Options, nosniff)? — раздел 1, /headers/
  2. Листинг каталогов выключен (Options -Indexes)? — раздел 3
  3. wp-config.php / .env / *.sql / *.bak закрыты? — раздел 2, /check/
  4. .git / .svn недоступны снаружи? — раздел 2
  5. Админка или staging под IP-фильтром либо паролем? — раздел 4, раздел 5, /htpasswd/
  6. В /uploads/ PHP не исполняется? — раздел 6
  7. X-Powered-By и версии PHP / Apache скрыты? — раздел 8
  8. В .htaccess нет php_value / php_flag, если сайт на PHP-FPM? — раздел 8, /check/
  9. Все блоки в синтаксисе Apache 2.4 (Require, не Order / Allow / Deny)? — /apache24/
  10. Хочешь, чтобы это проверили автоматически? — прогони свой .htaccess через аудит безопасности (он находит пункты из этого чеклиста + опасные Options/CORS/CSP).