Как собрать динамический модуль для Nginx с нуля
Соберём простой HTTP-модуль для Nginx 1.26.x, подключим его без пересборки самого сервера и проверим работу через заголовок ответа. Заодно разберём, где сборка ломается и почему версия бинарника важнее исходного кода.

За несколько минут можно собрать динамический модуль для Nginx, который добавляет HTTP-заголовок к успешным ответам. В примере используется Nginx 1.26.x и обычный модуль на C: исходный код компилируется отдельно, затем файл .so подключается директивой load_module. Такой подход не требует заменять рабочий бинарник Nginx, но требует точно сопоставить версию исходников и параметры сборки.
Что будем собирать
Модуль будет перехватывать фильтр заголовков ответа. Если upstream или сам Nginx вернул статус 200, модуль добавит:
X-Demo-Module: enabled
Это минимальный, но настоящий пример. Здесь есть точка входа модуля, HTTP-контекст, инициализация фильтра и динамическая библиотека, которую Nginx загрузит при старте.
Динамический модуль не является универсальным плагином: он должен быть собран под совместимую версию Nginx и набор его configure-параметров.
Два способа подключить модуль
| Способ | Что происходит | Цена решения |
|---|---|---|
| Статическая сборка | Исходники модуля добавляются в исходный код Nginx до общей компиляции | Нужно пересобирать и устанавливать весь Nginx при изменении модуля |
| Динамическая сборка | Модуль собирается в отдельный файл .so и загружается через load_module | Нужно следить за совместимостью бинарника и исходников Nginx |
Для первого модуля выбирайте динамический вариант. Он быстрее проверяется и позволяет откатить изменение удалением одной директивы и файла. Статическая сборка нужна, когда модуль должен быть частью базового образа или окружение запрещает загрузку внешних библиотек.
Динамический модуль1 — это разделяемая библиотека с расширением .so, которую Nginx загружает во время запуска, а не компилирует внутрь основного бинарника.
Что подготовить до начала
- установленный Nginx 1.26.x или точно известная версия рабочего бинарника;
- исходный архив той же версии Nginx;
- компилятор C и инструменты сборки;
- права на чтение вывода команды
nginx -Vи запись в каталог модулей; - тестовый endpoint, на котором можно проверить HTTP-заголовок.
Перед сборкой выполните:
nginx -V 2>&1
Сохраните строку configure arguments. Для рабочего сервера модуль лучше собирать с теми же ключами, что использовались при сборке Nginx, добавив --with-compat. Если Nginx установлен пакетом дистрибутива, исходники и готовый бинарник могут иметь разные параметры. Это частая причина ошибки загрузки.
Шаг 1. Создайте исходный код модуля
Создайте каталог ngx_http_demo_module рядом с каталогом исходников Nginx. Внутри создайте файл ngx_http_demo_module.c:
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static ngx_http_output_header_filter_pt ngx_http_demo_next_header_filter;
static ngx_int_t
ngx_http_demo_header_filter(ngx_http_request_t *request)
{
ngx_table_elt_t *header;
if (request->headers_out.status == NGX_HTTP_OK) {
header = ngx_list_push(&request->headers_out.headers);
if (header == NULL) {
return NGX_ERROR;
}
ngx_str_set(&header->key, "X-Demo-Module");
ngx_str_set(&header->value, "enabled");
header->hash = 1;
}
return ngx_http_demo_next_header_filter(request);
}
static ngx_int_t
ngx_http_demo_filter_init(ngx_conf_t *configuration)
{
ngx_http_demo_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_demo_header_filter;
return NGX_OK;
}
static ngx_http_module_t ngx_http_demo_module_ctx = {
NULL,
ngx_http_demo_filter_init,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ngx_module_t ngx_http_demo_module = {
NGX_MODULE_V1,
&ngx_http_demo_module_ctx,
NULL,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
Проверка шага: файл должен существовать, а в нём должны быть символы ngx_http_demo_module, ngx_http_demo_filter_init и ngx_http_demo_header_filter. Модуль намеренно работает только со статусом 200; ответы 301, 404 и 500 он не меняет.
Шаг 2. Добавьте файл конфигурации сборки
В том же каталоге создайте файл с именем config без расширения:
ngx_addon_name=ngx_http_demo_module
HTTP_MODULES="$HTTP_MODULES ngx_http_demo_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_demo_module.c"
Этот файл сообщает скрипту Nginx, что модуль относится к HTTP-подсистеме и какие исходники нужно включить. Путь в NGX_ADDON_SRCS не заменяйте на путь к установленному бинарнику: он должен вести к исходному файлу в каталоге модуля.
Проверка шага: в каталоге модуля лежат ровно два ключевых файла — config и ngx_http_demo_module.c.
Шаг 3. Соберите файл .so
Распакуйте исходники Nginx той же версии, с которой работает сервер. Ниже предполагается такая структура:
~/src/nginx-1.26.x/
~/src/ngx_http_demo_module/
Перейдите в каталог исходников и выполните конфигурацию. Для модуля без дополнительных библиотек достаточно:
cd ~/src/nginx-1.26.x
./configure --with-compat --add-dynamic-module=../ngx_http_demo_module
make modules
Если в выводе nginx -V есть дополнительные параметры, используйте их в этой команде. Не копируйте вслепую параметры от другой версии и не удаляйте --with-compat без причины.
Проверка шага: после успешной сборки появится файл objs/ngx_http_demo_module.so. Если make modules завершился ошибкой компилятора, библиотека не создана и переходить к установке рано.
Шаг 4. Установите и подключите модуль
Скопируйте библиотеку в каталог модулей, который использует ваша установка Nginx. Путь зависит от дистрибутива и способа установки. Пример с отдельным каталогом:
sudo install -D -m 0644 \
objs/ngx_http_demo_module.so \
/etc/nginx/modules/ngx_http_demo_module.so
В основной конфигурации Nginx добавьте директиву до блока http и до других конфигурационных блоков:
load_module /etc/nginx/modules/ngx_http_demo_module.so;
Путь в директиве должен совпадать с фактическим расположением файла. После изменения конфигурации проверьте её без перезапуска:
sudo nginx -t
Проверка шага: команда должна завершиться сообщениями syntax is ok и test is successful. Если проверка успешна, примените конфигурацию штатным способом вашей системы, например через reload службы.
Шаг 5. Проверьте результат
Отправьте запрос к endpoint, который гарантированно возвращает 200:
curl -I https://example.com/
В ответе должна появиться строка:
X-Demo-Module: enabled
Если заголовок не виден, сначала проверьте статус ответа и действительно ли запрос обслуживает тот экземпляр Nginx, в конфигурацию которого добавлен load_module. Для проверки можно временно посмотреть полный вывод nginx -T, но не публикуйте его целиком: в конфигурации могут быть внутренние адреса и секреты.
Что ломается чаще всего
«module is not binary compatible»
Модуль собран не под тот бинарник Nginx или использованы несовместимые параметры конфигурации. Сверьте версию исходников, вывод nginx -V и параметры ./configure. Пересоберите модуль из исходников той же версии.
«dlopen() ... failed»
Nginx не может открыть файл: неверный путь, отсутствуют права или не хватает системной зависимости. Проверьте путь в load_module, права чтения и вывод системного журнала. Не исправляйте проблему копированием библиотеки в случайный каталог.
«unknown directive load_module»
Рабочий Nginx собран без поддержки динамических модулей либо запускается другой бинарник. Сравните путь к исполняемому файлу, которым запускается служба, с результатом команды nginx -V. Если поддержка отсутствует, понадобится пакет или сборка Nginx с нужной конфигурацией.
Сборка завершается ошибкой заголовочного файла
Исходники модуля запускаются не через конфигурацию Nginx или каталог исходников неполный. Выполняйте ./configure из корня распакованного исходного дерева Nginx, а путь --add-dynamic-module направляйте на каталог, где лежит файл config.
Где этот подход ломается
Фильтр из примера применяется ко всем HTTP-ответам данного экземпляра Nginx и не знает, из какого location пришёл запрос. Если нужно менять заголовок только для одного маршрута, понадобится собственная конфигурационная директива и обработка настроек location — это уже отдельная версия модуля.
Не используйте такой модуль как единственный механизм безопасности. Заголовок легко удалить на следующем прокси, а ошибка в C-коде может завершить worker-процесс или повредить память. Перед установкой в продакшен прогоните модуль на тестовом бинарнике, проверьте reload и посмотрите error log при обычных и ошибочных ответах.
Что делать дальше
Следующий практический шаг — добавить модулю собственную директиву, например значение заголовка из конфигурации Nginx, и хранить её в структуре location-конфигурации. После этого измеряйте не только наличие заголовка, но и поведение при reload, ответах upstream с ошибками и нескольких worker-процессах. Документация Nginx по разработке модулей и исходники конкретной версии важнее примеров из старых блогов: API внутреннее, поэтому между версиями нужно проверять структуры и точки подключения.
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: Официальная документация Nginx: развёртывание динамических модулей, Официальное руководство Nginx: разработка модулей
Частые вопросы
Можно ли собрать модуль для Nginx 1.26.x из исходников другой версии?
Нужно ли пересобирать сам Nginx после изменения исходника модуля?
Почему nginx -t проходит, но заголовок не появляется?
Можно ли подключить модуль только к одному server или location?
Что делать, если после подключения Nginx перестал запускаться?
Безопасно ли использовать пример в продакшене?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.