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