Начну с небольшого вступления: зачем вообще нужен .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 как сервис для статики, а так же на него же возложить и динамические скрипты.

На этом все, спасибо за внимание!

Добавить комментарий

Предыдущая запись