.htaccess — конфигурационный файл Apache, который применяет правила к папке, в которой лежит, и ко всем вложенным. На shared-хостинге это единственный способ управлять сервером без доступа к главному конфигу — но каждая ошибка в синтаксисе кладёт весь сайт с ошибкой 500, поэтому редактировать файл нужно аккуратно и с резервной копией под рукой.
Принудительный HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Условие %{HTTPS} off срабатывает только для запросов без TLS, поэтому правило не зацикливается на уже защищённых страницах.
Флаги правил rewrite: что означают
| Флаг | Значение |
|---|---|
| L | Last — прекратить обработку остальных правил после этого |
| R=301 | Постоянный редирект (302 без числа — временный) |
| NC | No Case — регистр в условии не учитывается |
| QSA | Query String Append — сохранить GET-параметры при редиректе |
Без QSA ссылка вида example.com/page?utm_source=ads после редиректа потеряет параметр utm_source — это разрушает аналитику рекламных кампаний, поэтому для правил с параметрами флаг обязателен.
Редирект www → без www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
Обратный вариант (добавить www) требует зеркального правила: условие ловит хосты без www., а замена добавляет его в начало.
Частая ошибка: 500 Internal Server Error
После правки .htaccess сайт вдруг показывает ошибку 500. В error_log домена находится причина:
[Wed Sep 24 10:12:03 2026] [core:alert] [pid 8842] /home/user/public_html/.htaccess:
Invalid command 'RewriteEngin', perhaps misspelled or defined by a module
not included in the server configuration
Это опечатка в названии директивы (RewriteEngin вместо RewriteEngine) — Apache не может её распознать и останавливает обработку всего файла. Второй частый вариант той же ошибки — модуль mod_rewrite не подключён на сервере вовсе, тогда сообщение будет «not included in the server configuration» без опечатки в тексте директивы.
Кэширование в браузере
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
| Тип файла | Рекомендуемый срок | Почему |
|---|---|---|
| Изображения | 1 год | Меняются редко, версионируются через имя файла |
| CSS / JS | 1 месяц | Обновляются чаще, риск подать устаревшую версию |
| HTML | Без кэша или минуты | Контент страницы должен обновляться сразу |
| Шрифты | 1 год | Практически никогда не меняются |
GZIP сжатие
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
GZIP через mod_deflate уменьшает текстовые ответы на 60–80%, но не сжимает уже сжатые форматы — добавлять в список image/jpeg или video/mp4 бессмысленно, сервер только потратит CPU без выигрыша в размере.
Защита директории
# Запрет доступа к папке (Apache 2.2)
<Files *>
Order Deny,Allow
Deny from all
</Files>
# Защита wp-config.php
<Files wp-config.php>
Order Allow,Deny
Deny from all
</Files>
# Тот же запрет для Apache 2.4+
<Files wp-config.php>
Require all denied
</Files>
Директивы Order/Deny относятся к mod_access_compat из Apache 2.2 — на новых серверах с Apache 2.4 работает синтаксис Require all denied, и лучше использовать именно его, если хостинг это поддерживает.
Когда правила в .htaccess не сработают
- В главном конфиге сервера стоит
AllowOverride Noneдля директории — тогда Apache вообще не читает .htaccess, и никакие правки эффекта не дадут - Модуль
mod_rewriteилиmod_expiresотключён администратором — обратитесь в поддержку хостинга за подтверждением - Сайт работает на Nginx, а не на Apache — .htaccess Nginx не читает вообще, правила нужно переносить в конфиг server{}
- Провайдер использует LiteSpeed без модуля совместимости с Apache-директивами — часть редких правил (например, mod_security через .htaccess) там не поддерживается
Проверка результата
curl -I http://example.com/
# Ожидаем: HTTP/1.1 301 Moved Permanently
# Location: https://example.com/
Заголовок Location должен указывать на финальный HTTPS-адрес без www (или с www — в зависимости от выбранного канонического варианта), а код ответа — именно 301, а не 302, иначе поисковики не передадут вес страницы на новый адрес. Подробнее про типы SSL и настройку HTTPS — в статье редирект с HTTP на HTTPS, а про запрет чужих ссылок на картинки — в статье Hotlink Protection.