Функциональность ispmanager можно самостоятельно расширять с помощью механизма плагинов: добавлять новые пункты меню, формы, отчёты, а также перехватывать события до и после стандартных операций и интегрировать данные из внешних систем в интерфейс. Рассмотрим создание плагина для ispmanager на примере mod_rewrite в nginx. В результате доработки:
- В nginx подключается модуль-расширение mod_rewrite (ngx_http_apache_rewrite_module), который позволяет сайтам, работающим на связке nginx + PHP-FPM без Apache, корректно обрабатывать правила из .htaccess.
- Управление модулем становится доступно через интерфейс ispmanager без необходимости ручного редактирования конфигурационных файлов (исходные коды модуля-плагина).
Реализация ориентирована на AlmaLinux 9/10 и совместимые с ними дистрибутивы. В результате мы получим RPM-пакет, который решает две задачи:
- Загрузка и сборка модуля расширения.
Пакет скачивает исходные коды модуля mod_rewrite для nginx, собирает его и отслеживает версии: при обновлении системного nginx пересборка выполняется автоматически. - Интеграция с интерфейсом ispmanager.
В панели появляются формы управления настройками модуля, позволяющие администратору включать, отключать и конфигурировать расширение без обращения к командной строке.
Проверяем обработку правил .htaccess без плагина
Сначала проверим, как выполняются правила mod_rewrite в nginx через стандартных механизм ЧПУ, без плагина. Создадим в ispmanager тестового пользователя и сайт для него с файлом .htaccess, содержащим набор правил. Сайт настроен как FastCGI (nginx + PHP-FPM) с опцией «Включить обработку ЧПУ».

Структура тестового сайта:
./index.php
1 <?php
2 echo "Hello from index.php param=". $_GET["param"];
./test1.php
1 <?php
2 echo "hello from test1.php param = " . $_GET["param"];
./test2.php
1 <?php
2 echo "Hello from test2.php param=" . $_GET["param"];
3
4
./test3.php
1 <?php
2 echo "test3 should be forbidden";
.htaccess
1 RewriteEngine On
2 # --- Rule 1: Add ?param=1 to /index.php (if not already present) ---
3 RewriteCond %{QUERY_STRING} !^param=1 [NC]
4 RewriteRule ^index\.php$ /index.php?param=1 [L,R=302]
5 # --- Rule 2: /test1/* paths redirect to test1.php ---
6 RewriteRule ^test1/(.*)$ /test1.php [L]
7 # --- Rule 3: /test-ok/* paths redirect to test1.php ---
8 RewriteRule ^test-ok/(.*)$ /test1.php [L]
9 # --- Rule 4: /test6.php redirects to /test2.php ---
10 RewriteRule ^test6\.php$ /test2.php [L]
11 # --- Rule 5: /test3.php returns Forbidden (403) via RewriteRule ---
12 RewriteCond %{REQUEST_URI} ^/test3\.php [NC]
13 RewriteRule ^ / [F,L]Ожидаемое поведение правил:
- index.php должен перенаправляться на index.php?param=1
- Любой URL, начинающийся с test1/, должен вести на test1.php
- Любой URL, начинающийся с test-ok/, должен вести на test1.php
- test6.php должен внутренне перенаправляться на test2.php
- Доступ к test3.php должен быть запрещён (HTTP 403)
Связка Apache + mod_rewrite справляется с этими задачами без проблем. Проверим, как ведёт себя nginx + PHP-FPM с опцией «Включить обработку ЧПУ»:
Правило 1:
bash
# curl -L http://user2.test.domain
Hello from index.php param=Ожидалось: param=1 — не сработало.
Правило 2:
bash
# curl -L http://user2.test.domain/test1/1.php
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.30.2</center>
</body>
</html>Ожидался: вывод test1.php.
Правило 3:
bash
# curl -L http://user2.test.domain/test-ok/m.php?param=1
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.30.2</center>
</body>
</html>Ожидался: вывод test1.php.
Правило 4:
bash
# curl -L http://user2.test.domain/test6.php
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.30.2</center>
</body>
</html>Ожидался: вывод test2.php.
Правило 5:
bash
# curl -L http://user2.test.domain/test3.php
test3 should be forbiddenОжидался: HTTP 403 — вместо запрета отдаётся содержимое скрипта.
Результат очевиден: стандартный механизм «ЧПУ» не обеспечивает корректную обработку правил .htaccess. Без модификации конфигурации nginx многие CMS будут работать некорректно. Приступаем к решению проблемы.
Разработка плагина для ispmanager
Согласно официальной документации, создание плагина начинается с подготовки XML-файла, который определяет:
- структуру форм, отображаемых в интерфейсе;
- перечень событий, на которые плагин должен реагировать;
- обработчики этих событий.
Файлы плагина располагаются в следующих каталогах:
- /usr/local/mgr5/etc/xml — XML-описания структуры плагина;
- /usr/local/mgr5/addon — скрипты-обработчики логики.
Важно различать понятия модуль и плагин в терминологии ispmanager:
- Плагин расширяет функциональность панели, добавляя пункты меню и формы.
- Модуль появляется в разделе «Модули» и служит для первичной настройки — через него можно задать базовые параметры и удалить плагин через графический интерфейс.
В нашей статье фигурируют три сущности:
- Плагин ispmanager — добавляет пункты меню и формы управления.
- Модуль ispmanager — запись в разделе «Модули» для первичной настройки и удаления.
- Модуль mod_rewrite — расширение для nginx, которое собирается и устанавливается в систему.
Описание модуля ispmanager (первичная настройка)
Первичная настройка в нашем случае — это сборка модуля mod_rewrite для nginx.
Создаём описание модуля в файле /usr/local/mgr5/etc/plugins/ispmgr/nginx_mod_rewrite_plugin.xml:
xml
<?xml version="1.0" encoding="UTF-8" ?>
<mgrdata>
<plugin name="nginx_mod_rewrite_plugin">
<dist>1</dist>
<free />
<settings>nginx_mod_rewrite_plugin.settings</settings>
<msg name="desc_short" lang="ru">Модуль nginx mod_rewrite</msg>
<msg
name="desc_full"
lang="ru"
>Модуль добавления поддержки mod_rewrite правил и .htaccess в nginx<br/><a href="https://github.com/bayrepo/ngx_http_apache_rewrite_module" target="_blank">Документация по настройкам модуля</a></msg>
</plugin>
</mgrdata>Здесь мы указываем:
- через тег
<settings>— обработчик настройки модуля; - через тег
<free />— тип модуля (бесплатный); - краткое и полное описание на русском языке.
После перезапуска панели модуль появляется в разделе «Модули».

Форма настройки модуля
Теперь добавим описание формы настроек. Файл /usr/local/mgr5/etc/xml/ispmgr_mod_nginx_mod_rewrite_plugin.xml:
xml
<metadata name="nginx_mod_rewrite_plugin.settings" type="form" mgr="ispmgr">
<form>
<field name="nginx_mod_rewrite_plugin_field" noname="yes" fullwidth="yes">
<textdata name="nginx_mod_rewrite_plugin_msg" type="banner" status="warning"/>
<textdata name="nginx_mod_rewrite_plugin_msg_info" type="banner" status="info"/>
</field>
<buttons>
<button name="ok" type="ok" />
<button name="cancel" type="cancel" />
</buttons>
</form>
</metadata>Эта форма содержит информационный и предупреждающий баннеры, а также кнопки «ОК» и «Отмена». Что и когда отображать — определяет обработчик, прикреплённый к форме.
Обработчик указывается следующим образом:
xml
<handler name="nginx_mod_rewrite_plugin.py" type="xml">
<func name="nginx_mod_rewrite_plugin.settings" />
</handler>Таким образом, форма nginx_mod_rewrite_plugin.settings обрабатывается скриптом nginx_mod_rewrite_plugin.py, расположенным в /usr/local/mgr5/addon.
Обработчик модуля
Скрипт nginx_mod_rewrite_plugin.py написан на Python и выполняет системные действия в ответ на события от пользователя в веб-интерфейсе.
Его структура включает:
- логирование действий;
- сборку модуля mod_rewrite для nginx;
- установку флага, что сборка выполнена и можно активировать хук пересборки.
Хук пересборки прописывается в spec-файле:
spec
%triggerin -- nginx
if [ -e /usr/local/ispmanager-mod_rewrite/settings/installed.cfg ]; then
/usr/local/ispmanager-mod_rewrite/utils/build_mod_rewrite.py --nodeps
fiПри обновлении nginx этот триггер проверяет наличие файла-маркера и при необходимости запускает пересборку модуля.
В обработчике реализованы процедуры main и Handle. Они анализируют переменные окружения (PARAM_func, PARAM_plugin_settings) для распознавания обращения к форме настроек.
Скрипт читает XML из stdin, разбирает его и, если нажата кнопка «ОК»:
python
if os.getenv("PARAM_clicked_button") == "ok":
Handle(root)
запускает сборку модуля. При этом из XML удаляются ненужные баннеры:
python
for parent in root.iter():
for child in list(parent):
if child.tag == 'textdata' and child.get('name') == 'nginx_mod_rewrite_plugin_msg_info':
parent.remove(child)После завершения сборки в ответ добавляется элемент <ok>, который сигнализирует о закрытии формы:
python
etree.SubElement(root, 'ok')Пользовательский сценарий выглядит так:
- Переход в настройки модуля ispmanager.
- Нажатие кнопки сборки.
- Автоматическое закрытие формы после завершения.

При повторном заходе в настройки срабатывает другая ветка логики, которая сообщает, что модуль уже собран, и удаляет лишние кнопки и баннеры:

Модифицированный XML отправляется в stdout:
python
etree.dump(root)Описание форм управления mod_rewrite в плагине
После того как модуль mod_rewrite собран, необходимо обеспечить его включение для отдельных сайтов. Здесь есть два подхода.
Вариант 1. Использование include-директив
ispmanager позволяет добавлять собственные настройки для хостов nginx с помощью include. Типичная конфигурация хоста выглядит так:
text
server {
server_name userX.domain www.userX.domain;
charset off;
index index.php index.html;
disable_symlinks if_not_owner from=$root_path;
include /etc/nginx/vhosts-includes/*.conf;
include /etc/nginx/vhosts-resources/userX.domain/*.conf;
include /etc/nginx/users-resources/userX/*.conf;
...
}Для наших целей подходит путь /etc/nginx/vhosts-resources/userX.domain/*.conf. В этот каталог мы будем подкладывать файл с настройками mod_rewrite:
bash
# cat /etc/nginx/vhosts-resources/userX.domain/mod_rewrite.conf
GlobalLocationRewriteEngine on;
HtaccessEnable on;Эти директивы включают обработку .htaccess и активируют mod_rewrite для всех Location текущего сайта. Файл создаётся или удаляется при создании/редактировании сайта в зависимости от состояния чекбокса.
Вариант 2. Модификация шаблонов nginx
Этот способ сложнее, но даёт более тонкую настройку. Согласно документации ispmanager, шаблоны хостов хранятся в /usr/local/mgr5/etc/templates/default/nginx-vhosts.template и nginx-vhosts-ssl.template. Их можно скопировать в /usr/local/mgr5/etc/templates/ — там они не будут перезаписаны при обновлении панели.
Модифицируем шаблон для генерации следующей конфигурации:
text
server {
server_name domain;
...
location / {
...
RewriteEngine on;
}
location @php {
...
RewriteEngine on;
}
...
HtaccessEnable on;
}Для повышения гибкости статические файлы можно вынести за пределы корневого Location, чтобы исключить применение правил mod_rewrite к изображениям, CSS и JS. Это снижает нагрузку и даёт более предсказуемую маршрутизацию.
При таком подходе нужно учитывать, что изменения в шаблонах по умолчанию могут потребовать ручного обновления. Это можно автоматизировать с помощью патчей, которые накладываются при активации mod_rewrite.
Форма глобальных настроек плагина
Для управления плагином создаём форму глобальных настроек, где администратор может:
- отключить загрузку mod_rewrite в nginx;
- выбрать способ интеграции (Вариант 1 или Вариант 2);
- включить отладку работы плагина.
Описание формы:
xml
<metadata name="nginx_mod_rewrite_plugin_menu_settings" type="form">
<form needconfirm="yes">
<field name="nginx_mod_rewrite_plugin_menu_module_enable">
<input type="checkbox" name="nginx_mod_rewrite_plugin_menu_module_enable"/>
</field>
<field name="nginx_mod_rewrite_plugin_menu_global_module_enable">
<input type="checkbox" name="nginx_mod_rewrite_plugin_menu_global_module_enable"/>
</field>
<field name="nginx_mod_rewrite_plugin_menu_debug_enable">
<input type="checkbox" name="nginx_mod_rewrite_plugin_menu_debug_enable"/>
</field>
</form>
</metadata>Также необходимо добавить чекбокс в форму редактирования сайта:
xml
<metadata name="webdomain.edit" type="form">
<form>
<page name="optimization">
<field name="nginx_mod_rewrite_plugin_site_edit_enable">
<input type="checkbox" name="nginx_mod_rewrite_plugin_site_edit_enable"/>
</field>
</page>
</form>
</metadata>Этот флажок появится на вкладке «Оптимизация» при редактировании сайта.
Для навигации создаём пункт меню:
xml
<mainmenu level="admin+">
<modernmenu>
<node name="nginx_mod_rewrite_plugin_menu" customicon="/manimg/icons/mod_rewrite_menu.svg">
<node name="nginx_mod_rewrite_plugin_menu_settings" customicon="/manimg/icons/mod_rewrite_tool.svg"/>
</node>
</modernmenu>
</mainmenu>

Теперь добавляем обработчик для новых форм:
xml
<handler name="nginx_form_rewrite_plugin.py" type="xml">
<event name="webdomain.edit" after="yes"/>
<func name="nginx_mod_rewrite_plugin_menu_settings"/>
</handler>Здесь func обрабатывает глобальное меню, а event расширяет стандартную функцию webdomain.edit, срабатывая после неё.
Обработчик форм плагина
Скрипт nginx_form_rewrite_plugin.py, расположенный в /usr/local/mgr5/addon, анализирует переменные окружения и определяет, какое действие нужно выполнить:
- включить или отключить mod_rewrite;
- выбрать способ встраивания (шаблон или include);
- включить/отключить отладку;
- обработать создание или изменение сайта.
При переключении флажка «Использовать шаблон» происходит копирование и модификация шаблонов nginx.
Форма создания сайта с установленным плагином выглядит так:

При включении чекбокса для сайта обработчик либо создаёт файл /etc/nginx/vhosts-resources/userX.domain/mod_rewrite.conf, либо устанавливает переменную для шаблона:
python
new_element = etree.Element("nginx_mod_rewrite_plugin_site_edit_enable")
new_element.text = "on"
root.append(new_element)В шаблоне /usr/local/mgr5/etc/templates/nginx-vhosts-ssl.template появляется условие:
text
location / {
{% if $nginx_MOD_REWRITE_PLUGIN_SITE_EDIT_ENABLE == on %}
RewriteEngine on;
{% endif %}
Сборка пакета плагина
Важное замечание: плагин рекомендуется устанавливать именно через RPM-пакет, а не путём ручного копирования файлов. Пакет устанавливает триггер (хук), который отслеживает обновления nginx и запускает пересборку mod_rewrite автоматически.
Сборка RPM-пакета выполняется с помощью Docker. Достаточно одной команды:
bash
bash build_package.sh almalinux:9где almalinux:9 может быть заменён на любой образ RPM-совместимой ОС.
После успешной сборки в каталоге _packages появляются rpm-файлы:
text
_packages/ispmanager-plugin-nginx_mod_rewrite_plugin-0.0.1-1.el9.src.rpm
_packages/ispmanager-plugin-nginx_mod_rewrite_plugin-0.0.1-1.el9.x86_64.rpmНа этом этап сборки завершён.
Результат работы mod_rewrite
Активируем mod_rewrite для нашего тестового сайта и проверяем те же правила.
Правило 1:
bash
# curl -L http://user2.test.domain
Hello from index.php param=1✅ Работает корректно.
Правило 2:
bash
# curl -L http://user2.test.domain/test1/1.php
hello from test1.php param =✅ Работает корректно.
Правило 3:
bash
# curl -L http://user2.test.domain/test-ok/m.php?param=1
hello from test1.php param = 1✅ Работает корректно.
Правило 4:
bash
# curl -L http://user2.test.domain/test6.php
Hello from test2.php param=✅ Работает корректно.
Правило 5:
bash
# curl -L http://user2.test.domain/test3.php
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.30.2</center>
</body>
</html>✅ Работает корректно.
Все правила отрабатывают так, как и ожидалось.
Теперь сайты на CMS, использующие .htaccess, могут работать на связке nginx + PHP-FPM в окружении ispmanager так же предсказуемо, как и на Apache, а администраторам не приходится вручную править конфигурационные файлы.