PHPFlowPHP, MySQL, Laravel и JavaScript

Файл .htaccess: редиректы, https, www, ЧПУ, запрет доступа и кеширование

Обновлено:

Коротко

Файл .htaccess - настройки Apache для папки сайта: в нём делают 301 редирект, переход с http на https, склейку www, ЧПУ, запрет доступа и кеширование. Ниже - готовые блоки, которые можно вставить в файл в корне сайта, с пояснением каждой строки. Помните: Nginx .htaccess не читает, а ошибка в файле ломает весь сайт кодом 500, поэтому перед правкой сохраняйте копию.

Содержание
  1. Что такое файл .htaccess и где он работает
  2. 301 редирект в .htaccess
  3. Редирект с http на https
  4. Сайт с www или без www
  5. ЧПУ: красивые адреса через .htaccess
  6. Запрет доступа: deny from all, IP и файлы
  7. Кеширование и сжатие
  8. Другие полезные настройки
  9. Как проверить .htaccess и найти ошибку

Что такое файл .htaccess и где он работает

Файл .htaccess - это файл настроек веб-сервера Apache для отдельной папки. Его кладут в корень сайта (или в любую подпапку), и Apache применяет правила к этой папке и всему, что внутри. Через .htaccess делают 301 редирект, переход с http на https, склейку www, ЧПУ, запрет доступа к файлам, кеширование и сжатие - без доступа к главному конфигу сервера, что важно на виртуальном хостинге.

Схема обработки .htaccess в Apache
Как Apache обрабатывает .htaccess: правило редиректа срабатывает до запуска PHP и сразу возвращает новый адрес

Что нужно знать до начала:

  • Имя файла начинается с точки и не имеет расширения. В Windows создайте его в редакторе кода через "Сохранить как" с именем .htaccess. В FTP-клиенте включите показ скрытых файлов - иначе, например, WinSCP не видит .htaccess.
  • Файл работает только в Apache и совместимых серверах (LiteSpeed). Nginx .htaccess не читает: на сервере только с Nginx те же правила пишут в конфиг сайта.
  • Чтобы Apache читал файл, в конфиге виртуального хоста должна быть директива AllowOverride All для папки сайта. На хостинге она обычно уже включена.
  • Для редиректов и ЧПУ нужен модуль mod_rewrite. На своём сервере с Ubuntu он включается командой sudo a2enmod rewrite.

Одна ошибка в .htaccess - и весь сайт отдаёт код 500. Перед правкой сохраните копию файла. Если сайт упал, верните копию или переименуйте файл; подробная диагностика - в статье ошибка 500 Internal Server Error.

Правила читаются сверху вниз. Для ориентира - общий порядок блоков в файле: сначала защита и запреты, затем редиректы (https, www, перенесённые страницы), затем ЧПУ, в конце кеширование и сжатие.

301 редирект в .htaccess

Код 301 говорит браузеру и поисковикам, что страница переехала навсегда: Яндекс и Google передают новому адресу накопленный вес и со временем заменяют адрес в выдаче. Временный 302 для переезда не подходит.

Редирект одной страницы

Самый простой способ - директива Redirect из модуля mod_alias:

# Старый адрес → новый (путь от корня, новый - полностью)
Redirect 301 /old-page.html https://site.ru/new-page/
Redirect 301 /blog/2019/sale/ https://site.ru/akcii/

Для адресов с параметрами (?id=15) и шаблонов нужен mod_rewrite:

RewriteEngine On
# /product.php?id=15 → /catalog/15/
RewriteCond %{QUERY_STRING} ^id=(\d+)$
RewriteRule ^product\.php$ /catalog/%1/? [R=301,L]

# Целый раздел: /news/любой-адрес → /blog/любой-адрес
RewriteRule ^news/(.*)$ /blog/$1 [R=301,L]

Знак ? в конце нового адреса отбрасывает старую строку параметров. Флаг L останавливает обработку правил для этого запроса.

Редирект на другой домен с сохранением пути

При переезде сайта на новый домен каждая старая страница должна вести на ту же страницу нового домена, а не на главную. Положите .htaccess в корень старого сайта:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?old-site\.ru$ [NC]
RewriteRule ^(.*)$ https://new-site.ru/$1 [R=301,L]

Проверьте результат командой curl -I https://old-site.ru/catalog/: в ответе должны быть HTTP/1.1 301 и строка Location: https://new-site.ru/catalog/.

Проверка редиректа командой curl -I
Проверка 301 редиректа на другой домен: в заголовке Location тот же путь на новом домене

Пока настраиваете правила, ставьте R=302: браузер надолго запоминает 301, и после исправления ошибки вы будете видеть старый редирект. Когда всё работает, поменяйте на R=301. Проверяйте в режиме инкогнито или через curl.

Редирект с http на https

После подключения SSL-сертификата все адреса с http:// должны вести одним 301 на https://. Стандартное правило:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

На многих хостингах перед Apache стоит Nginx, который сам принимает HTTPS, а Apache получает запрос уже по http. Тогда %{HTTPS} всегда равен off, и правило выше даёт бесконечный редирект - браузер покажет ERR_TOO_MANY_REDIRECTS. В этом случае проверяйте заголовок, который передаёт Nginx:

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

Обратную переадресацию с https на http делают редко - например, если сертификат перестал работать. Это временная мера: без HTTPS браузеры помечают сайт как небезопасный.

Сайт с www или без www

Для поисковиков site.ru и www.site.ru - разные адреса. Выберите один вариант главным зеркалом и перенаправьте второй. Лучше сразу совместить с https, чтобы любая комбинация приводила на правильный адрес за один редирект, без цепочек:

RewriteEngine On
# Всё (http, www) → https://site.ru одним 301
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC,OR]
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

Если нужен вариант с www, правило зеркальное:

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC,OR]
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^(.*)$ https://www.%1/$1 [R=301,L]

Проверьте все четыре варианта главной: http://site.ru, http://www.site.ru, https://www.site.ru и https://site.ru. Первые три должны отдавать один 301 сразу на главное зеркало.

ЧПУ: красивые адреса через .htaccess

ЧПУ (человекопонятные URL) - адреса вида /catalog/phones/ вместо /index.php?page=catalog&cat=7. Современные фреймворки и CMS используют схему "единой точки входа": если запрошенного файла или папки нет, запрос передаётся в index.php, а PHP разбирает адрес сам.

RewriteEngine On
RewriteBase /
# Существующие файлы и папки отдаём как есть
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Всё остальное - в index.php
RewriteRule ^ index.php [L]

В index.php адрес берётся из $_SERVER['REQUEST_URI']:

<?php
declare(strict_types=1);

// Путь без строки параметров: /catalog/phones/
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? '/';
$parts = array_values(array_filter(explode('/', $path)));

match ($parts[0] ?? '') {
    ''        => require __DIR__ . '/pages/home.php',
    'catalog' => require __DIR__ . '/pages/catalog.php',
    default   => http_response_code(404),
};

Как устроен такой роутер в связке с контроллерами и шаблонами, показано в статье MVC на чистом PHP.

Слеш в конце адреса

Адреса со слешем и без - тоже дубли. Добавить слеш ко всем адресам, кроме файлов:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ /$1/ [R=301,L]

Это правило ставят выше блока ЧПУ, иначе запрос уйдёт в index.php раньше редиректа.

Стандартный .htaccess WordPress и Laravel

WordPress создаёт свой блок между метками # BEGIN WordPress и # END WordPress и перезаписывает его при сохранении "Настройки → Постоянные ссылки". Свои правила (редиректы, защиту) пишите выше этого блока, иначе они пропадут. Если файл испорчен, удалите блок и нажмите "Сохранить изменения" на странице постоянных ссылок - WordPress создаст правильный заново.

В Laravel .htaccess лежит в папке public, и корень сайта на хостинге должен указывать именно на неё. Файл из коробки уже содержит правила для ЧПУ и передачу заголовка Authorization; менять его почти не нужно.

Запрет доступа: deny from all, IP и файлы

В Apache 2.4 доступ настраивается директивой Require. Старая запись Deny from all из Apache 2.2 работает только при подключённом модуле совместимости, поэтому в новых файлах используйте Require all denied - смысл тот же: "никого не пускать".

# Закрыть всю папку (например, /uploads/backup/)
Require all denied

# Пустить только свой IP - например, в папку админки
Require ip 203.0.113.25

# Заблокировать IP и подсеть, остальных пускать
<RequireAll>
    Require all granted
    Require not ip 198.51.100.7
    Require not ip 192.0.2.0/24
</RequireAll>

Защита служебных файлов

Файлы .env, .git, дампы баз и логи не должны открываться из браузера. Закройте их в корневом .htaccess:

# Скрытые файлы и папки: .env, .git, .htpasswd, сам .htaccess
<FilesMatch "^\.">
    Require all denied
</FilesMatch>
# Дампы, логи и резервные копии
<FilesMatch "\.(sql|log|bak|ini)$">
    Require all denied
</FilesMatch>
# Отключить список файлов в папках без index
Options -Indexes
Защита служебных файлов в .htaccess
Блок защиты служебных файлов в корневом .htaccess: .env, .git, дампы и логи не откроются из браузера

Пароль на папку

Чтобы закрыть тестовую версию сайта паролем, создайте файл паролей вне папки сайта и подключите его:

# Создать файл и пользователя (утилита из пакета apache2-utils)
htpasswd -c /home/user/.htpasswd admin
AuthType Basic
AuthName "Закрытый раздел"
AuthUserFile /home/user/.htpasswd
Require valid-user

Путь в AuthUserFile - полный путь на сервере, а не адрес на сайте.

Кеширование и сжатие

Браузер может хранить картинки, стили и скрипты у себя и не скачивать их при каждом заходе. Это ускоряет сайт и улучшает оценку PageSpeed. За срок хранения отвечает модуль mod_expires, за сжатие - mod_deflate. Оборачивайте директивы в <IfModule>: если модуля нет, сайт не упадёт с ошибкой 500.

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

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

Если вы надолго кешируете CSS и JS, после каждой правки меняйте версию в ссылке: style.css?v=2. Иначе посетители ещё месяц будут видеть старый файл. Проверить, что сжатие работает, можно так:

curl -sI -H "Accept-Encoding: gzip" https://site.ru/style.css | grep -i -E "content-encoding|expires|cache-control"

Другие полезные настройки

Свои страницы ошибок

ErrorDocument 404 /404.html
ErrorDocument 500 /500.html

Страница 404 должна отдаваться именно с кодом 404, а не перенаправлять на главную: иначе поисковики увидят сотни дублей главной.

Настройки PHP: память и размер загрузки

Запрос "htaccess переопределить память" встречается часто, но директивы php_value memory_limit 256M работают только если PHP подключён как модуль Apache (mod_php). На большинстве современных хостингов PHP работает через FPM, и такая строка даёт ошибку 500. Надёжнее положить в корень сайта файл .user.ini:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M

Все варианты, где менять настройки PHP и как убедиться, что они применились, - в статье php.ini: где находится.

Кодировка по умолчанию

AddDefaultCharset UTF-8

Как проверить .htaccess и найти ошибку

  1. После каждого изменения откройте сайт в режиме инкогнито и пару внутренних страниц.
  2. Проверьте редиректы командой curl -IL адрес: она покажет всю цепочку. Хороший результат - один 301 и затем 200.
  3. Если сайт отдаёт 500, посмотрите лог ошибок Apache: там будет строка файла .htaccess с ошибкой.
  4. Если правила не срабатывают вовсе, проверьте, что включены mod_rewrite и AllowOverride All.
Проверка цепочки редиректов curl -IL
Правильная склейка зеркал: с http и www один 301 сразу на https://site.ru, без цепочки редиректов

Локально удобно тренироваться в XAMPP: там Apache и .htaccess работают так же, как на хостинге, а ошибка не уронит рабочий сайт.

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

Где находится файл .htaccess?

В корне сайта, обычно в public_html или www. Файл скрытый: включите показ скрытых файлов в FTP-клиенте или файловом менеджере хостинга. Если файла нет, его можно создать.

Как сделать редирект с http на https в .htaccess?

Добавьте RewriteEngine On, условие RewriteCond %{HTTPS} off и правило RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]. Если перед Apache стоит Nginx, проверяйте заголовок X-Forwarded-Proto.

Что значит deny from all в .htaccess?

Это запрет доступа к папке для всех посетителей в синтаксисе Apache 2.2. В Apache 2.4 то же самое пишется как Require all denied.

Как заблокировать IP в .htaccess?

Внутри блока <RequireAll> укажите Require all granted и строку Require not ip 198.51.100.7. Подсеть задаётся так: 192.0.2.0/24.

Почему .htaccess не работает?

Сервер работает на Nginx, который не читает .htaccess, в конфиге Apache не включён AllowOverride All или не подключён mod_rewrite. Проверьте и то, что файл назван точно .htaccess, без расширения .txt.

Можно ли увеличить память PHP через .htaccess?

Только если PHP работает как модуль Apache: php_value memory_limit 256M. При PHP-FPM эта строка вызовет ошибку 500, используйте файл .user.ini.