Начну с небольшого вступления: зачем вообще нужен .htaccess файл?
.htaccess — это конфигурационный файл, который использует веб‑сервер Apache для задания специфических настроек для каждой отдельной папки сайта. Благодаря ему можно:
- Перенаправлять URL‑ы (используя модуль mod_rewrite), чтобы «чистые» адреса были удобнее для пользователя и поисковых систем.
- Описывать правила доступа к папкам (базовая аутентификация, ограничение по IP и так далее).
- Настраивать обработку ошибок (страницы 404, 500 и др.).
- Включать или отключать функции PHP, менять заголовки, задавать правила кэширования и многое другое.
Главная причина существования .htaccess — это гибкость и простота. Владелец сайта может внести нужные изменения без доступа к глобальному конфигурационному файлу сервера. После изменения .htaccess Apache автоматически применяет новые правила к запрашиваемой папке, что делает процесс обновления быстрым и без необходимости перезапуска сервера.
А что такое CMS и зачем CMS нужен .htaccess?
CMS (Content Management System) — это программный пакет, который упрощает создание, редактирование, хранение и публикацию веб‑контента без необходимости вручную писать HTML‑код. Система обычно включает:
- Удобный интерфейс для авторов и администраторов.
- Шаблоны (темы), позволяющие менять внешний вид сайта без изменения кода.
- Модульность — расширения, плагины, модули, которые добавляют новые функции.
- База данных для хранения страниц, статей, медиа‑файлов и настроек.
На просторах интернета можно найти огромное количество CMS, написанных на PHP (CMS других языков пока оставлю за контекстом статьи), которые для корректной работы требуют наличие mod_rewrite; для него разработчик CMS в файле .htaccess размещает правила обработки запроса, указывая, какую страницу и куда отправлять, как трансформировать URL или запретить доступ к определенным каталогам. Это относится к любой популярной CMS — WordPress, Drupal, Grav и т. д.
Почему они стали так популярны? Потому что любой хостинг позволяет просто развернуть CMS для своего сайта, и после этого сайт заработает, получит красивый интерфейс и позволит неопытному пользователю создать свою персональную страницу или целый сайт, доступный в сети, с минимальными знаниями в программировании.
Ну и как сказано ранее, владелец сайта с помощью mod_rewrite с .htaccess может определить доступ к структуре сайта без перезагрузки всего сервиса.
Изучая исходные коды контрольной панели HestiaCP, а так же настройку конфигурации веб сервисов у других контрольных панелей по управлению сервером, были выделены несколько основных подходов к развертыванию сайтов — это Nginx + php-fpm или Nginx + Apache + (php-fpm, mod_fcgid, mod_php).
Вторая конфигурация для настройки сайтов используется наиболее часто, где Nginx стоит как фронт энд для обработки статических файлов. А динамические запросы передаются на Apache для дальнейшей обработки.
Таким образом решается проблема с .htaccess для сайта (опять же замечу, что в рамках статьи не рассматриваю .user.ini, который позволяет настраивать php_value и php_flag в .htaccess, это пока что вне статьи).
Первый вариант – Nginx + php‑fpm – менее популярен, когда нужно стандартизировать и унифицировать настройки для большого числа сайтов. Хотя эта конфигурация более оптимальна (исключаются лишние соединения с Apache), она имеет существенный недостаток: Nginx не обрабатывает .htaccess. В результате стандартные CMS часто работают некорректно, и теряется возможность использовать ЧПУ – человекопонятные URL.
Проанализировав как решаются проблемы .htaccess в контрольных панелях можно прийти к выводу, что проблема решается либо через:
try_files $uri $uri/ index.php?$args- в HestiaCP (и некоторых других панелях) используется подход, основанный на шаблонах конфигурации для модуля ngx_http_rewrite_module. Для каждой CMS, которую планируется развернуть на сайте, создаётся собственный шаблон настроек server для сайта, в котором правила трансформации URL описываются в синтаксисе, аналогичном mod_rewrite. По данной ссылке приведён пример шаблона для Nginx, применяемого к Drupal. И такой шаблон разработан для каждого продукта использующего .htaccess.
Первый вариант только с try_files решает от 50% до 80% проблем работы CMS и этого не достаточно. Второй вариант закрывает все, но жестко привязывает сайт к конкретной CMS не давая пользователю проявить фантазию и что-то изменить (в случае, если пользователь сайта не является администратором сервера).
Соответственно, появилась идея попробовать реализовать для Nginx что-то похожее на mod_rewrite с поддержкой .htaccess.
Вот код получившегося модуля и документация.
Моменты которые бы хотелось описать:
- Механизм проваливания в index.php или настраиваемый скрипт был позаимствован у try_files, в данном случае результаты работы алгоритмов схожи и у Nginx и у Apache.
- Реализованы настройки конфигурации на уровне server, location и на уровне .htaccess. Настройки server отдельные, а настройки location и htaccess объединяются, такой подход позволяет не трансформируя конфигурацию переносить ее прямо в директивах mod_rewrite в конфигурацию Nginx.
- Для того, чтоб уменьшить количество чтений .htaccess - он читается при запросе и сохраняется в pool запроса.
- Механизм внутреннего редиректа (когда не указано R=301 и т.д) реализуется так же с помощью механизма internal redirect как в try_files.
- RewriteMap — сделана минимальная реализация, так как тестируемые CMS не требовали наличие данной директивы.
- RewriteOptions — реализация для server и location. А на уровне location + .htaccess действие директивы упрощено, пока просто объединение, первые правила .htaccess потом loction. Поведение данной опции не глубоко прорабатывалось, так как не обнаружена её необходимость для CMS.
Сборка модуля описана в данном документе.
Ее можно произвести несколькими способами, как на самой машине с исходными кодами Nginx, так и в docker контейнере, с получением на выходе rpm пакета или deb пакета для версии nginx, специфичной для операционной системы под которой производилась сборка.
Упрощенный пример из документации по сборке для Almalinux 9:
- установить в системе docker
- клонировать проект и в корне проекта выполнить команду:
bash package_preparer.sh prepare "almalinux:9"
- после сборки собранные пакеты появятся в каталоге tmpbuild:
$ ls -1 tmpbuild/*.rpm
tmpbuild/nginx-mod-rewrite-0.1-1.el9.src.rpm
tmpbuild/nginx-mod-rewrite-0.1-1.el9.x86_64.rpm
tmpbuild/nginx-mod-rewrite-debuginfo-0.1-1.el9.x86_64.rpm
tmpbuild/nginx-mod-rewrite-debugsource-0.1-1.el9.x86_64.rpm
или
$ ls -1 tmpbuild/nginx-mod-rewrite*
tmpbuild/nginx-mod-rewrite_0.1-1_amd64.buildinfo
tmpbuild/nginx-mod-rewrite_0.1-1_amd64.changes
tmpbuild/nginx-mod-rewrite_0.1-1_amd64.deb
tmpbuild/nginx-mod-rewrite_0.1-1.dsc
tmpbuild/nginx-mod-rewrite_0.1-1.tar.gz
tmpbuild/nginx-mod-rewrite-0.1.tar.gz
tmpbuild/nginx-mod-rewrite-dbgsym_0.1-1_amd64.deb
Пример настройки хоста для работы с mod_rewrite:
server {
listen 127.0.0.1:80;
server_name test.my.brp www.test.my.brp;
root /home/test/web/test.my.brp/public_html;
index index.php index.html index.htm;
...
HtaccessEnable on;
location ~ /\.(?!well-known\/) {
deny all;
return 404;
}
location ~* ^.+\.(jpeg|jpg|png|webp|gif|bmp|ico|svg|css|js|txt)$ {
expires max;
fastcgi_hide_header "Set-Cookie";
}
location / {
RewriteEngine On;
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php74-fpm.sock;
}
}
...
}
То есть, на уровне блока server включается поддержка .htaccess – директивой HtaccessEnable on. Далее, в блоке location /, активируется работа mod_rewrite. А в блоке location ~* \.(jpeg|jpg|png|webp|gif|bmp|ico|svg|css|js|txt)$ URL обрабатывается непосредственно без участия mod_rewrite, что позволяет исключить лишний разбор файла .htaccess.
Естественно, что за такие финты как разбор .htaccess нужно платить, поэтому привожу ниже проверку насколько упала скорость обработки запроса при подключении mod_rewrite в Nginx, по сравнению с чистым Nginx, а так же в сравнении со связкой Nginx+Apache+php-fpm.
Для теста бралась готовая установка Drupal.
- Первый тест — это тестирование страницы с проваливанием в index.php - запрос примерно такого вида
http://test4.my.brp/ - Второй тест — это проверка статического файла (оговорюсь, что и в Apache, и в Nginx+mod_rewrite была включена обработка статики в mod_rewrite, выше пример конфигурации, где статика не обрабатывается mod_rewrite и это ускоряет работу) - запрос такого вида
http://test4.my.brp/robots.txt - Третий тест — это когда несуществующий путь пробегается по правилам и в конечном итоге перенаправляется в index.php - запрос такого вида
http://test4.my.brp/user/login
Для того что-бы чистый Nginx+php-fpm без mod_rewrite не выдавал 404, для теста был активирован механизм try_files.
| Nginx + php-fpm | Nginx + mod_rewrite + php-fpm | Nginx + apache + php-fpm | |
|---|---|---|---|
http://test4.my.brp/ |
Requests per second: 129.74 [#/sec] |
Requests per second: 125.94 [#/sec] |
Requests per second: 121.31 [#/sec] |
http://test4.my.brp/robots.txt |
Requests per second: 7384.25 [#/sec] |
Requests per second: 4591.31 [#/sec] |
Requests per second: 2007.70 [#/sec] |
http://test4.my.brp/user/login |
Requests per second: 126.48 [#/sec] |
Requests per second: 128.58 [#/sec] |
Requests per second: 117.78 [#/sec] |
Хочу заметить, что данные тесты не направлены показать, на сколько быстрее данное решение, чем решение с Apache, так как можно из связки Nginx+Apache+php-fpm выкинуть Nginx и php-fpm и оставить только Apache+mod_php.
Данные тесты показывают, что решение Nginx+mod_rewrite не хуже чем Nginx+Apache+php-fpm, но при этом упрощает настройку, когда нужно настраивать только Nginx и php-fpm для сайта, исключив настройку Apache и давая возможность вообще его не использовать, оставляя возможность работать Nginx как сервис для статики, а так же на него же возложить и динамические скрипты.
На этом все, спасибо за внимание!