Skip to content
EN
← Все статьи

Файл .htaccess: где лежит, как работает и как настроить редиректы, доступ и кэш

Коротко. .htaccess — это файл конфигурации Apache, который действует на каталог, где он лежит, и на все вложенные каталоги. Apache перечитывает его на каждом запросе, поэтому правки применяются сразу, без перезапуска. Он работает только если администратор разрешил переопределение директивой AllowOverride, и полностью игнорируется nginx.

Что такое .htaccess и когда он вообще работает

Apache читает основной конфиг один раз при старте. Кроме него он умеет читать «распределённые» файлы конфигурации — по одному на каталог. Имя такого файла по умолчанию .htaccess. Смысл механизма в том, чтобы владелец сайта на общем хостинге мог менять поведение веб-сервера в своём каталоге, не имея доступа к главному конфигу и не имея права перезапускать сервис.

Три условия, без которых файл не сработает вообще:

  • Сайт обслуживает Apache (или совместимый с ним LiteSpeed). Чистый nginx не читает .htaccess ни при каких настройках — это принципиальное архитектурное отличие, а не недоработка.
  • Разрешено переопределение. В конфиге виртуального хоста для вашего каталога должно стоять AllowOverride со значением, отличным от None. Если стоит None, Apache даже не откроет файл — он будет лежать и молча ничего не делать.
  • Подключён нужный модуль. Директива RewriteRule без mod_rewrite, ExpiresByType без mod_expires и Header без mod_headers дают не «правило не сработало», а ошибку 500 на весь каталог.

Значение AllowOverride задаёт не «да/нет», а список групп директив: FileInfo (редиректы и rewrite), AuthConfig (парольная защита), Limit (доступ по IP), Indexes (листинг каталога), Options. Отсюда типичная ситуация на хостинге: редиректы работают, а Options -Indexes выдаёт 500 — разрешена одна группа, но не другая.

Где лежит .htaccess и как его создать

Канонического пути нет: файл лежит в корне сайта, а корень у каждого хостинга называется по-своему — public_html, www, htdocs, httpdocs, site/public. Ориентируйтесь не на имя каталога, а на то, где лежит index.php или index.html, отдаваемый по адресу сайта.

Имя начинается с точки, поэтому файл скрытый. В файловом менеджере панели и в FTP-клиенте включите показ скрытых файлов, иначе будете уверены, что файла нет, и создадите второй.

# создать пустой файл и посмотреть, что уже есть в корне
cd /var/www/example.com/public_html
ls -la | grep -i htaccess
printf '' > .htaccess
chmod 644 .htaccess

Требования к самому файлу простые, но именно на них ломаются правки:

  • Кодировка — UTF-8 без BOM. Три невидимых байта BOM в начале файла Apache считает мусором до первой директивы и отвечает 500.
  • Переводы строк — LF. Файл, сохранённый Блокнотом с CRLF, обычно работает, но в директивах с продолжением строки и в кавычках даёт непредсказуемые эффекты.
  • Права 644, владелец — пользователь, от которого работает сайт. Файл с правами 600 и чужим владельцем веб-сервер прочитать не сможет.
  • Каждая директива — на своей строке, комментарии начинаются с #. Комментарий в конце строки с директивой недопустим: он будет разобран как аргумент.
Перед любой правкой сделайте копию. cp .htaccess .htaccess.bak-$(date +%F) — это одна секунда, а откатывать сломанный редирект по памяти на проде вы будете долго. Общий подход к резервным копиям — в материале про бэкап сайта.

Как Apache применяет директивы: каскад и наследование

Получив запрос, Apache определяет путь к файлу на диске и проходит по всем каталогам от корня документов до целевого, читая .htaccess в каждом. Директивы объединяются: файл в подкаталоге дополняет родительский, а при конфликте — переопределяет его.

Из этого следуют два неочевидных практических вывода.

Первый: правила mod_rewrite по умолчанию не наследуются. Если в корне описан фронт-контроллер, а в подкаталоге вы создали свой .htaccess с любым RewriteRule, набор правил родителя для этого подкаталога перестаёт применяться целиком. Вернуть наследование можно директивой RewriteOptions Inherit, но чаще правильнее не плодить файлы, а держать все правила в корневом.

Второй: RewriteBase задаёт базовый путь для правил с относительной заменой. При переносе сайта из подкаталога в корень (или наоборот) забытый RewriteBase — самая частая причина того, что «на старом хостинге всё работало».

Внутри одного файла директивы применяются сверху вниз. Флаг [L] прекращает обработку текущего прохода, но если правило сделало внутреннюю подмену пути, Apache запускает набор правил заново — отсюда циклы, о которых ниже.

Схема: HTTP-запрос проходит от корня документов по каталогам, в каждом Apache читает файл конфигурации и объединяет директивы
Apache читает конфигурацию каталогов каскадом: от корня документов до целевого каталога, объединяя директивы на каждом уровне.

Редиректы: HTTPS, склейка www и отдельные страницы

Для простых случаев «путь → путь» хватает Redirect из mod_alias — он быстрее и читается однозначно. RewriteRule нужен там, где решение зависит от условий: схемы, хоста, наличия файла, заголовка.

# Простой постраничный переезд (mod_alias)
Redirect 301 /old-page.html /new-page/
RedirectMatch 301 ^/blog/([0-9]+)/(.*)$ /articles/$2

# Канонический адрес одним хопом (mod_rewrite)
RewriteEngine On

# 1) www → без www, сразу на https
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

# 2) http → https; за обратным прокси схему сообщает заголовок, а не %{HTTPS}
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Порядок правил здесь не случаен. Запрос на http://www.example.com/page попадает в первое правило и уходит на https://example.com/page за один переход. Если поменять правила местами, тот же запрос сделает два перехода: сначала на https с www, потом на https без www. Лишний хоп — это лишний RTT для пользователя и размытие сигнала для поисковых систем; разбор в материалах про 301 и 302 и редиректы и SEO.

Отдельно про %{HTTPS}. Если перед Apache стоит nginx, балансировщик или CDN, TLS терминируется на них, и до Apache запрос доходит по http. Переменная %{HTTPS} в этом случае всегда равна off, а условие «если не https — редиректить» выполняется на каждом запросе. Результат — бесконечный цикл и ERR_TOO_MANY_REDIRECTS у всех посетителей. Проверять нужно заголовок, который выставляет прокси.

Схема сравнения: цепочка из трёх редиректов против одного прямого перехода на канонический адрес
Правильный порядок правил склеивает схему и хост за один переход вместо цепочки из двух-трёх.

Доступ: запрет по IP, пароль на каталог, защита служебных файлов

Синтаксис контроля доступа поменялся между Apache 2.2 и 2.4, и это источник большинства «внезапных» пятисоток при переезде. Старые директивы Order, Allow from, Deny from в 2.4 работают только при подключённом модуле совместимости mod_access_compat; если его нет — ошибка конфигурации.

# Apache 2.4 — актуальный синтаксис
<RequireAll>
    Require all granted
    Require not ip 203.0.113.10
    Require not ip 198.51.100.0/24
</RequireAll>

# Apache 2.2 — устаревший синтаксис (нужен mod_access_compat)
Order allow,deny
Allow from all
Deny from 203.0.113.10

Пароль на каталог состоит из двух частей: файла с хешами паролей и директив, которые на него ссылаются. Файл с хешами нельзя держать внутри публичного каталога — иначе его можно скачать.

# создать файл с паролем ВНЕ корня сайта
htpasswd -c /var/www/example.com/.htpasswd admin

# .htaccess в защищаемом каталоге
AuthType Basic
AuthName "Restricted area"
AuthUserFile /var/www/example.com/.htpasswd
Require valid-user

Путь в AuthUserFile обязан быть абсолютным — относительный путь даёт 500. Флаг -c у htpasswd создаёт файл заново, стирая прежних пользователей; для добавления второго пользователя запускайте команду без него.

Basic-аутентификация передаёт логин и пароль в заголовке при каждом запросе. Без HTTPS это открытый текст в сети. Ставьте её только на сайте с рабочим сертификатом — проверить его можно на странице проверки SSL. Второй нюанс: если у приложения есть собственная авторизация, оно тоже читает заголовок Authorization, и две системы могут конфликтовать — вплоть до выкидывания из админки.

Служебные файлы стоит закрывать явно, не полагаясь на то, что «о них никто не знает». Типовой набор — конфиги окружения, дампы, логи, бэкапы и каталоги систем контроля версий:

# запретить отдачу служебных файлов
<FilesMatch "^\.|\.(env|ini|log|sql|sql\.gz|bak|old|sh)$">
    Require all denied
</FilesMatch>

# выключить листинг каталога, если в нём нет индексного файла
Options -Indexes

# запретить выполнение PHP в каталоге загрузок
<FilesMatch "\.(php|phtml|php[0-9])$">
    Require all denied
</FilesMatch>

Последний блок кладут в каталог пользовательских загрузок. Это одна из немногих мер, которая реально останавливает веб-шелл, загруженный через дырявую форму: файл на диск попадёт, но выполнить его через веб не получится. Что делать, если заражение уже случилось — в разборе проверки сайта на вирусы.

Схема слоёв защиты каталога: фильтр по IP, парольная защита, запрет отдачи служебных файлов и запрет выполнения скриптов в загрузках
Контроль доступа собирается слоями: сетевой фильтр, парольная защита, запрет на отдачу служебных файлов и на выполнение скриптов в каталоге загрузок.

Кэширование, сжатие и заголовки ответа

Если у сайта нет доступа к конфигу сервера, .htaccess — единственное место, где можно управлять кэшированием статики и сжатием. Оба механизма дают заметный эффект и оба легко настроить неправильно.

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml
    AddOutputFilterByType DEFLATE application/javascript application/json
    AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType image/webp "access plus 6 months"
    ExpiresByType image/svg+xml "access plus 6 months"
    ExpiresDefault "access plus 1 hour"
</IfModule>

<IfModule mod_headers.c>
    Header set X-Content-Type-Options "nosniff"
    Header set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>

Обёртка IfModule здесь не украшение: без неё отсутствие модуля кладёт весь каталог в 500. Год кэширования безопасен только для файлов с версией в имени или в query-строке — иначе посетители получат старый CSS до истечения срока. HTML долго кэшировать нельзя: контент меняется, а пользователь будет видеть прошлую версию страницы.

Сжимать уже сжатое бессмысленно и вредно: изображения в JPEG, PNG, WebP, видео и архивы тратят процессорное время без выигрыша в размере. Что даёт компрессия на разных типах контента, разобрано в сравнении gzip и Brotli, а стратегии кэша — в материале про кэширование веб-приложений.

ЧПУ и фронт-контроллер: блок CMS, который нельзя править вручную

Почти все CMS используют одну и ту же схему: если запрошенного файла и каталога физически нет на диске, запрос отдаётся входному скрипту, а тот сам разбирает адрес.

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Две строки-маркера BEGIN и END — не комментарий для человека, а границы, по которым CMS находит свой блок и перезаписывает его при сохранении настроек постоянных ссылок. Всё, что вы впишете внутрь этих строк, однажды исчезнет без предупреждения. Свои правила добавляйте выше блока (если они должны отработать раньше) или ниже, но никогда не внутри.

Порядок здесь имеет значение: редирект на канонический адрес логично держать выше блока CMS, чтобы он срабатывал до передачи управления входному скрипту.

Ошибка 500 после правки и другие типичные поломки

Главное, что нужно знать про диагностику: apachectl configtest проверяет основной конфиг и не заглядывает в .htaccess. Ошибка в распределённом файле проявляется только в момент запроса и пишется в лог ошибок сайта. Поэтому первый шаг всегда один — открыть лог.

# последние ошибки Apache
tail -n 50 /var/log/apache2/error.log    # Debian, Ubuntu
tail -n 50 /var/log/httpd/error_log      # RHEL, AlmaLinux, CentOS

# цепочка редиректов и заголовки ответа
curl -sIL https://example.com | grep -i -E '^HTTP/|^location:'
curl -sI https://example.com/assets/style.css | grep -i -E 'cache-control|content-encoding'

Что означают самые частые сообщения:

Симптом или строка в логеПричинаЧто делать
Invalid command 'RewriteEngine'Не подключён mod_rewriteВключить модуль; на общем хостинге — запросить у поддержки
Invalid command 'php_value'PHP работает как FPM или CGI, а не модулем ApacheПереносить настройки PHP в .user.ini или в панель хостинга
.htaccess: Option Indexes not allowed hereДиректива вне разрешённых групп AllowOverrideРасширить AllowOverride в конфиге или отказаться от директивы
500 сразу после сохранения, лог пустBOM в начале файла или битая кодировкаПересохранить как UTF-8 без BOM с переводами строк LF
ERR_TOO_MANY_REDIRECTSУсловие редиректа истинно и после редиректаПроверять X-Forwarded-Proto за прокси; убрать пересекающиеся правила
403 на весь каталогRequire all denied шире, чем задумано, или нет индексного файла при Options -IndexesСузить условие; разбор — в материале про ошибку 403
Правило просто не срабатываетAllowOverride None, файл не в том каталоге, правило перекрыто более ранним [L]Проверить каталог и порядок правил

Если лог не даёт ответа, работает грубый, но быстрый метод: закомментировать половину файла и повторить запрос. Две-три итерации локализуют строку точнее, чем перечитывание конфига глазами. Общий разбор пятисоток — в материале про ошибку 500.

Чем .htaccess платит за удобство

Основной конфиг Apache разбирает один раз при старте и держит в памяти. Распределённые файлы устроены иначе: при AllowOverride, отличном от None, сервер на каждом запросе проверяет наличие файла в каждом каталоге по пути — от корня документов до целевого — и разбирает найденное заново. Для страницы с сотней статических файлов это сотни лишних обращений к файловой системе.

Отсюда практическое правило: если доступ к конфигу виртуального хоста есть, правила нужно переносить туда, а в каталоге ставить AllowOverride None. Это не микрооптимизация ради красоты — на нагруженном сайте разница видна в графике времени ответа. Как искать причину замедления системно, описано в материале про высокую нагрузку на сервер, а разница между тарифами с доступом к конфигу и без — в сравнении хостинга, VPS и выделенного сервера.

Сравнение: конфигурация виртуального хоста читается один раз при старте, распределённые файлы каталогов перечитываются на каждом запросе
Конфиг виртуального хоста разбирается один раз при старте; распределённые файлы каталогов сервер перечитывает на каждом запросе.

Хостинг на nginx: .htaccess не работает — что делать

nginx не поддерживает распределённые файлы конфигурации по своей архитектуре и не будет их поддерживать. Файл в корне сайта на nginx — просто текстовый файл, который никто не читает. Правила переносятся в server-блок и перечитываются командой перезагрузки конфигурации; синтаксис и структура разобраны в руководстве по настройке nginx, а куда смотреть при отладке — в материале про логи nginx.

Отдельно стоит связка, где nginx стоит перед Apache: динамику проксирует Apache, а статику nginx отдаёт сам. В такой схеме правила из .htaccess применяются к PHP-страницам и не применяются к картинкам и стилям — заголовки кэширования для статики придётся задавать в nginx. Понять, что именно перед вами, помогает определение технологий сайта и заголовок Server в ответе.

Как проверить, что правила применились

Правка .htaccess считается сделанной не тогда, когда файл сохранён, а тогда, когда изменение видно в ответе сервера. Быстрый круг проверок:

Проверяйте не только главную. Редирект часто ломает частные случаи: адреса с query-строкой, вложенные каталоги, кириллические пути, страницы с завершающим слешем и без него.

Частые вопросы

Почему .htaccess не работает, хотя файл лежит в корне?

Три причины по частоте: сайт обслуживает nginx, который такие файлы не читает; в конфиге стоит AllowOverride None; файл лежит не в том каталоге — например, на уровень выше корня документов. Проверить первое можно по заголовку Server в ответе, второе — только через поддержку хостинга или конфиг виртуального хоста.

Можно ли использовать .htaccess на nginx?

Нет. Это не вопрос настройки: nginx не читает конфигурацию из каталогов сайта принципиально, ради производительности. Правила нужно переписать в server-блок и перезагрузить конфигурацию.

Как закрыть сайт от индексации через .htaccess?

Директивой Header set X-Robots-Tag "noindex, nofollow" из mod_headers. В отличие от robots.txt, этот способ работает и для файлов, которые не являются HTML: PDF, изображений, дампов. Важно не совмещать его с запретом в robots.txt на тот же адрес — если краулеру запрещено скачивать страницу, он не увидит и заголовок.

Нужен ли .htaccess, если есть доступ к конфигу сервера?

Нет, и лучше без него. Всё, что делает распределённый файл, делает конфиг виртуального хоста — быстрее и с проверкой синтаксиса до перезапуска. Держать .htaccess имеет смысл там, где доступа к конфигу нет или где правила должен менять человек без прав администратора.

Почему после правки редиректа браузер зациклился, а curl показывает норму?

Браузер кэширует ответ 301 надолго, иногда до очистки данных сайта. Проверяйте изменения в приватном окне или инструментом, который не хранит кэш редиректов, и не ставьте 301, пока правило не проверено: заменить его на 302 задним числом сложно — старый ответ уже сохранён у пользователей.

Где лежит .htaccess у WordPress и что будет, если его удалить?

В корне сайта, рядом с wp-config.php. Без него постоянные ссылки перестанут открываться: все адреса, кроме главной, вернут 404, потому что запросы больше не передаются входному скрипту. Файл восстанавливается автоматически при повторном сохранении настроек постоянных ссылок, если у веб-сервера есть права на запись в каталог.

Чеклист правки .htaccess

  • Сделана резервная копия файла с датой в имени.
  • Файл лежит в корне документов, сохранён в UTF-8 без BOM, права 644.
  • Каждый блок с директивами модуля обёрнут в IfModule.
  • Редиректы на канонический адрес приводят к цели за один переход.
  • За обратным прокси схема определяется по X-Forwarded-Proto, а не по %{HTTPS}.
  • Синтаксис контроля доступа соответствует версии Apache (Require для 2.4).
  • Файл с паролями лежит вне публичного каталога, путь в AuthUserFile абсолютный.
  • Служебные файлы и выполнение скриптов в каталоге загрузок закрыты.
  • Собственные правила добавлены вне блока BEGIN и END вашей CMS.
  • После правки проверены цепочка редиректов, заголовки ответа и несколько нетипичных адресов, а не только главная страница.

Проверьте ваш сайт прямо сейчас

Следить за своим сервером →
Другие статьи: Инфраструктура
Инфраструктура
Почта на своём домене: Яндекс 360, VK WorkSpace или свой сервер — настройка, отправка с сайта и переезд
21.07.2026 · 302 просм.
Инфраструктура
Алгоритмы балансировки нагрузки: Round Robin, Least Connections и другие
16.03.2026 · 265 просм.
Инфраструктура
Стратегии версионирования API: URL, заголовки и параметры запроса
16.03.2026 · 262 просм.
Инфраструктура
Rate Limiting в API: зачем и как настроить
14.03.2026 · 255 просм.