Перейти к содержанию

cgi: Поддержка CGI для NGINX

Установка

Вы можете установить этот модуль в любой дистрибутив на базе RHEL, включая, но не ограничиваясь:

  • RedHat Enterprise Linux 7, 8, 9 и 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 и Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-cgi
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-cgi

Включите модуль, добавив следующее в начало файла /etc/nginx/nginx.conf:

load_module modules/ngx_http_cgi_module.so;

Этот документ описывает nginx-module-cgi v0.15, выпущенный 5 марта 2026 года.


Добавляет поддержку CGI для Nginx и Angie веб-сервера.

ОС Протестировано с Nginx Angie
Linux AlmaLinux 9, Debian 12 и Ubuntu 24.04/20.04 нормально нормально
Darwin MacOS 15.1 нормально нормально
BSD FreeBSD 14.2 и OpenBSD 7.6 нормально нормально
Solaris OmniOS r1510521 нормально нормально
Windows Нет планов, nginx едва поддерживает Windows

Перед всем

CGI не является ни демоном, ни ангелом. Это просто инструмент. Так же, как нож шеф-повара в руках повара или меч в руках воина, вы не будете использовать меч для готовки, и не возьмете нож шеф-повара на поле боя. То же самое касается CGI, у него есть свои подходящие сценарии, и его не следует злоупотреблять или демонизировать.

CGI хорош для:

  • Приложений с низкой частотой, таких как управление системой
  • Систем с ограниченными ресурсами, такими как встраиваемые системы
  • Проектов с низким бюджетом, таких как личные веб-сайты
  • Прототипирования для быстрого итерационного процесса

CGI плох для:

  • Высокого QPS
  • Высокого трафика
  • Высокой конкурентности

Я создал канал в Discord. Если:

  • Вы также любите CGI
  • У вас есть проблемы с nginx-cgi
  • Вы хотите получать обновления о nginx-cgi
  • Вы хотите познакомиться с новыми друзьями

Пожалуйста, присоединяйтесь к нам: https://discord.gg/EJSfqHHmaR.

Бенчмарк

CGI не так медленен, как обычно думают люди. На самом деле его основные ограничения связаны с накладными расходами на процессы и изменчивостью задержки — не с тем, что он может обрабатывать лишь несколько запросов в минуту.

Я провел простой тест.

Тестовая среда:

  • Машина: недорогой экземпляр Vultr ($5/месяц) (1 общий vCPU ~2.3GHz, 1GB RAM)
  • ОС: Debian 12
  • Команда теста для CGI: ab -n 1000 -c 100 127.0.0.1/cgi-bin/hello.sh
  • Команда теста для простого текста: ab -n 1000 -c 100 127.0.0.1/hello.txt

Результаты:

  • hello.sh: 1007 req/s
  • hello.txt: 4891 req/s

Быстрый старт

Добавьте репозиторий (пример для Ubuntu - замените 'ubuntu' и 'jammy' на ваш дистрибутив)

echo "deb [signed-by=/etc/apt/keyrings/getpagespeed.gpg] https://extras.getpagespeed.com/ubuntu jammy main" \ | sudo tee /etc/apt/sources.list.d/getpagespeed-extras.list

Получите исходный код

git clone https://github.com/pjincz/nginx-cgi cd nginx-cgi

Инициализируйте rpmbuild

rpmdev-setuptree cd ~/rpmbuild

Скачайте файл спецификации из репозитория

wget https://github.com/pjincz/nginx-cgi/raw/refs/heads/main/fedora/nginx-cgi.spec

!/bin/bash

echo "Content-Type: text/plain" echo

echo "Добро пожаловать в мир CGI!"

Не забудьте добавить права на выполнение к CGI-скрипту:

```sh
chmod +x your-document-root-dir/cgi-bin/hello.sh

Перезапустите nginx и попробуйте:

systemctl restart nginx
curl http://127.0.0.1/cgi-bin/hello.sh

Если все прошло успешно, вы увидите приветствие из мира CGI. :D

Использование

Загрузка плагина

Если этот плагин установлен в стандартный путь модуля nginx (например, /usr/lib/nginx/modules), плагин будет загружен автоматически. В противном случае вам нужно вручную загрузить плагин с помощью load_module.

Добавьте следующее утверждение в верхний уровень контекста nginx для загрузки плагина:

load_module <dir-of-plugin>/ngx_http_cgi_module.so;

Включение cgi

После загрузки плагина вы можете добавить cgi on в контексты location для включения cgi. Пример:

location /cgi-bin {
    cgi on;
}

Как только cgi включен в location, все вложенные locations также будут иметь включенный cgi. Если вы хотите отключить cgi для дочернего location, просто используйте cgi off.

Когда location будет доступен, nginx-cgi найдет скрипт в корне документа (он указан в директиве root). Например, если вы указали корень документа как /var/www/html, то при доступе к /cgi-bin/hello.sh будет выполнен /var/www/html/cgi-bin/hello.sh.

Nginx-cgi также поддерживает alias, это похоже на директиву root в nginx, единственное отличие в том, что префикс location будет удален из uri. Например, если вы хотите, чтобы /cgi/hello.sh также ссылался на тот же скрипт, вы можете сделать это:

location /cgi {
    alias /var/www/html/cgi-bin;
    cgi on;
}

Скрипт hello

CGI-скрипт может быть написан на любом языке. Вот пример на shell. Вы можете сохранить его в /var/www/html/cgi-bin/hello.sh для тестирования (если вы не изменили корень документа по умолчанию):

#!/bin/sh

echo "Status: 200 OK"
echo "Content-Type: text/plain"
echo

echo "Привет, мир"

Первая строка скрипта — это шебанг. Если вы явно установили cgi_interpreter, эту строку можно удалить, в противном случае отсутствие шебанга приведет к ошибке 500. Некоторые оболочки позволяют скрипту быть исполняемым даже без шебанга, но здесь это не разрешено. Если скрипт может выполняться оболочкой, но возвращает ошибку 500, проверьте шебанг.

Вывод CGI-скрипта состоит из 2 частей: заголовка и тела. Первые 2 оператора echo выводят заголовок, а последний оператор echo выводит тело. Оператор echo посередине выводит разделитель. Оба раздела могут быть пустыми, но разделитель обязателен. Отсутствие разделителя приведет к ошибке 500.

Все строки в заголовке будут обработаны как обычные строки заголовка HTTP-ответа и переданы на клиентскую сторону. Существует один специальный заголовок Status, который будет передан в строку статуса ответа. Если cgi_strict включен, nginx-cgi проверит все заголовки вывода cgi, и если будет найден недопустимый заголовок, будет возвращена ошибка 500. В противном случае недопустимые заголовки также будут переданы на клиентскую сторону. Настоятельно рекомендуется оставить cgi_strict включенным.

После разделителя весь вывод будет отправлен клиенту как тело в том виде, в каком он есть.

Права на выполнение

В конце концов, вам нужно добавить права на выполнение к файлу:

chmod +x /var/www/html/cgi-bin/hello.sh

Обычно вам нужны права на выполнение, чтобы сделать скрипт исполняемым. Отсутствие прав на выполнение может привести к ошибке 403. Если по какой-либо причине вы не можете это сделать, cgi_interpreter может помочь.

Заголовок запроса

Заголовки запроса будут обработаны и затем переведены в переменные окружения и переданы CGI-скрипту.

Например, вы можете найти строку запроса в переменной окружения QUERY_STRING. И получить доступ к Http-Accept через HTTP_ACCPET.

Вот пример:

#!/bin/sh
echo ""

echo "строка запроса: $QUERY_STRING"
echo "http accept: $HTTP_ACCEPT"

Для полного списка переменных окружения смотрите раздел о переменных окружения.

Тело запроса

Тело запроса будет передано через stdin. Вот пример, чтобы прочитать все тело запроса и отобразить его:

#!/bin/sh
echo ""

body=$(cat)

echo "тело запроса: $body"

Потоковая передача

Nginx-cgi поддерживает потоковую передачу как для тела запроса, так и для тела ответа. Например, мы можем реализовать самый простой онлайн-калькулятор с помощью bc:

#!/bin/sh
echo ""

bc 2>&1

Затем мы можем протестировать наш калькулятор с помощью curl:

curl 127.0.0.1/cgi-bin/bc.sh --no-progress-meter -T .

Плагин nginx-cgi достаточно умен, чтобы выбрать правильный способ возврата тела запроса. Если он получил весь вывод достаточно быстро, он выведет тело за один раз. Если вывод задерживается, он будет выводить тело частями (HTTP 1.1) или потоково (HTTP 1.0).

Заголовки HTTP hop-by-hop

Заголовки HTTP hop-by-hop не допускаются в выводе CGI-скрипта. Если они появляются в ответе, клиенту будет возвращена ошибка 500.

Для получения дополнительной информации: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#hop-by-hop_headers

Хитрости и часто задаваемые вопросы

Я хочу перечислить все переменные окружения

Поместите следующий скрипт в ваш каталог CGI и выполните curl из вашего терминала:

#!/bin/sh

echo 'Content-Type: text/plain'
echo

printenv

Я хочу права root

Поместите файл sudo в /etc/sudoers.d и выполните sudo в вашем скрипте или установите cgi_interpreter как /usr/bin/sudo.

Вот пример файла конфигурации sudo:

# разрешить wwww-data запускать /var/www/bin/my-danger-script с правами root
www-data ALL=(root) NOPASSWD: /var/www/bin/my-danger-script

# разрешить всем CGI-скриптам запускаться с sudo напрямую через nginx-cgi
www-data ALL=(root) NOPASSWD: SETENV: /var/www/html/cgi-bin/*

Как я могу запускать CGI-скрипты с chroot

Настоятельно не рекомендуется запускать CGI-скрипты с chroot. Потому что chroot не предназначен для обеспечения безопасности. Он по-прежнему делит много пространств ядра с хост-системой. Например, запустив ps -ef в процессе chroot, все процессы в хост-системе будут возвращены. Это не должно быть слишком ужасно? Нет, это действительно ужасно, потому что вы также можете выполнять kill в скрипте chroot по той же причине. И люди обычно запускают программы с правами root в окружении chroot. Это ужасно плохо. Это ставит систему под высокий риск, чем просто запуск скрипта с www-data.

Если вам нужна песочница, lxc, docker и jails гораздо лучше для этой цели.

Если вы все еще хотите chroot, хорошо, позвольте мне показать вам, как это сделать.

В этом примере я предполагаю, что вы используете /var/www/html в качестве корня документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

# попробуйте это
/var/www/html/cgi-bin/ls.sh

Шаг 1: подготовьте каталог chroot.

Существует множество способов выполнить этот шаг. debootstrap является популярным способом в системах на базе debian. busybox — самый легкий способ. docker — современный способ.

Давайте создадим самый легкий каталог с помощью busybox:

# В этом примере я помещаю все в /var/www/chroot
# Будьте осторожны, я загружаю версию busybox для x86_64, вам может потребоваться изменить ее
# Вам нужны права root для выполнения всех следующих команд, мне слишком лень
# добавлять sudo ко всем командам здесь.

root_dir=/var/www/chroot

mkdir -p "$root_dir/bin" && cd "$root_dir/bin"
wget https://www.busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
chmod +x busybox

cd "$root_dir"
mkdir -p $(dirname $(./bin/busybox --list-full) | sort -u)
./bin/busybox --list-full | while read line; do ln -sf /bin/busybox $line; done

# попробуйте это
chroot "$root_dir" ls

Шаг 2: смонтируйте корень документа в каталог chroot

mkdir -p /var/www/chroot/var/www/html
mount --bind /var/www/html /var/www/chroot/var/www/html

# попробуйте это
ls /var/www/chroot/var/www/html

Обратите внимание:

  • Я использую здесь трюк, после chroot корень документа остается прежним. Таким образом, мы можем сэкономить время на сопоставлении путей.

  • Монтирование не будет сохраняться после перезагрузки. Вам может потребоваться добавить запись в /etc/fstab. Или переместить /var/www/html в chroot и сделать символическую ссылку снаружи.

Шаг 3: разрешите www-data запускать chroot с правами root.

cat >/etc/sudoers.d/www-run-with-chroot <<EOF
# разрешить и только разрешить www-data запускать chroot с /var/www/chroot
www-data ALL=(root) NOPASSWD: /usr/sbin/chroot /var/www/chroot *
EOF

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/bin/sudo /usr/sbin/chroot /var/www/chroot;
}

Попробуйте это:

curl 127.0.0.1/cgi-bin/ls.sh

Как я могу запускать CGI-скрипты с docker

В этом примере я предполагаю, что вы используете /var/www/html в качестве корня документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

# попробуйте это
/var/www/html/cgi-bin/ls.sh

Создайте контейнер и запустите его в фоновом режиме:

# Измените -v, если необходимо
# -d: запускает в фоновом режиме
# -i -t: сохраняет терминал
# --restart always: поддерживает контейнер в живых
docker run -dit --restart always --name my_cgi_docker -v /var/www:/var/www busybox sh

# попробуйте это
docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh

Разрешите www-data выполнять команды docker:

sudo usermod -aG docker www-data

# попробуйте это
sudo -u www-data docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/bin/docker exec my_cgi_docker;
}

Как я могу запускать CGI-скрипты с jails

Хорошо, вы фанат FreeBSD? Я тоже.

Это действительно похоже на запуск скриптов с помощью chroot.

Здесь я предполагаю, что вы также используете /var/www/html в качестве корня документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

# попробуйте это
/var/www/html/cgi-bin/ls.sh

Шаг 1: создайте тюрьму

Давайте поместим тюрьму в /var/www/jail.

mkdir -p /var/www/jail && cd /var/www/jail
fetch https://download.freebsd.org/ftp/releases/$(uname -m)/$(uname -m)/$(uname -r)/base.txz
tar -xvf base.txz -C .

# создайте точки монтирования
mkdir -p /var/www/jail/var/www/html
touch /var/www/jail/etc/resolv.conf

Поместите следующую конфигурацию в /etc/jail.conf:

www-jail {
    path = "/var/www/jail";
    host.hostname = "www-jail.local";

    exec.clean;
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";

    # смонтировать /var/www/html => /var/www/jail/var/www/html
    exec.prestart += "mount_nullfs /var/www/html /var/www/jail/var/www/html || true";
    mount.devfs;

    # раскомментируйте следующие строки, если хотите разрешить доступ к сети в тюрьме
    # ip4 = inherit;
    # ip6 = inherit;
    # exec.prestart += "mount_nullfs /etc/resolv.conf /var/www/jail/etc/resolv.conf || true";

    # раскомментируйте следующие строки, если хотите, чтобы `ping` был доступен в тюрьме
    # allow.raw_sockets = 1;

    persist; # сохранять тюрьму, если процессы не работают
}

И убедитесь, что следующая строка присутствует в /etc/rc.conf:

jail_enable="YES"

И запустите тюрьму:

service jail start www-jail

# попробуйте это
jexec www-jail ls /
jexec www-jail /var/www/html/cgi-bin/ls.sh

Шаг 2: разрешите www запускать jexec с правами root.

Я использую sudo здесь. Я не знаком с doas, если вы предпочитаете doas, вы можете попробовать это сами. В любом случае, ни sudo, ни doas не предустановлены в FreeBSD. Вам нужно будет вручную установить один из них.

cat >/usr/local/etc/sudoers.d/www-jexec <<EOF
# разрешить и только разрешить `www` запускать `jexec` с `www-jail`
www ALL=(root) NOPASSWD: /usr/sbin/jexec www-jail *
EOF

# попробуйте это
sudo -u www sudo jexec www-jail /var/www/html/cgi-bin/ls.sh

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/local/bin/sudo /usr/sbin/jexec www-jail;
}

Попробуйте это:

curl 127.0.0.1/cgi-bin/ls.sh

Я хочу создать длительный фоновый процесс

Просто убедитесь, что не наследуете stdout, когда создаете процесс (в идеале, избегайте наследования stdin и stderr также). Вот пример, написанный на shell.

taskid=1234
logfile="/var/lib/my-project/$taskid"
./long-run-task.sh "$taskid" </dev/null >"$logfile" 2>&1 &

Или, если вы знакомы с операцией pipe, просто закройте stdout (также лучше закрыть stdin и stderr), http-запрос будет завершен немедленно. И вы можете использовать процесс как фоновый процесс.

exec </dev/null >somewhere 2>&1

# теперь http-ответ завершен, делайте что угодно
sleep 9999

Мой http-запрос завис

Как вы видите выше. В мире CGI жизненный цикл http-запроса зависит от жизненного цикла pipe (stdout).

Каждый дочерний процесс может наследовать pipe процесса CGI. Если какой-либо процесс, унаследовавший stdout, остается активным, HTTP-запрос никогда не завершится.

Это может вызвать путаницу, когда вы хотите запустить длительный фоновой процесс или убить процесс CGI.

Для создания длительного процесса смотрите вышеуказанную тему.

Для убийства процесса CGI убивайте всю группу процессов, а не сам процесс CGI.

cgi_pid=...

# не делайте это
# kill "$cgi_pid"

# делайте это
kill -- "-$cgi_pid"

Я хочу убить свой CGI-скрипт

Смотрите вышеуказанную тему.

Я хочу динамически генерировать контент

Традиционно люди используют переписывание для достижения этого. Но здесь это гораздо проще. Вы можете сделать это с помощью cgi pass. Вот пример для динамической отрисовки markdown:

{
    location ~ ^.*\.md$ {
        cgi_pass /var/www/bin/cgi/render-markdown.sh;
    }
}
#!/bin/sh

set -e

if [ ! -f "${DOCUMENT_ROOT}${PATH_INFO}" ]; then
    echo "Status: 404"
    echo
    exit
fi

echo "Status: 200"
echo "Content-Type: text/html"
echo

echo "<html><body>"
markdown "${DOCUMENT_ROOT}${PATH_INFO}"
echo "</body></html>"

Мне не нравятся суффиксы в URL

Способ 1: Удаление суффикса CGI-скрипта

Способ 2: переписывание

Способ 3: cgi pass

Как я могу ответить статусом, отличным от 200

#!/bin/sh

echo "Status: 404"
echo "Content-Type: text/plain"
echo

echo "Добро пожаловать в пустоту"

Как я могу ответить перенаправлением

#!/bin/sh

echo "Status: 302"
echo "Location: https://theuselessweb.com"
echo

Как я могу получить тело http-запроса

Вы можете прочитать тело запроса из stdin. Если вы используете shell, cat может быстро сохранить тело запроса в файл.

Как я могу отправить файл клиенту

Для небольших файлов вы можете записать файл непосредственно в stdout.

Для больших файлов гораздо лучше отправить ответ 302. Поскольку ответ CGI является потоковым, протокол не может легко обрабатывать кэширование, загрузки частями или поддержку возобновления.

Я хочу писать CGI на python, ruby, perl, C, C++...

Делайте это. Nginx-cgi не заботится о том, на каком языке вы пишете. Просто извлекайте информацию из переменных окружения, читайте тело запроса из stdin и записывайте вывод в stdout.

Руководство

Опции

cgi <on|off> или cgi pass <script_path> [script_args...]

Включает или отключает модуль cgi в заданном блоке location.

Если вы укажете on, плагин будет работать в традиционном режиме. Он сначала разбирает uri запроса, а затем находит скрипт в каталоге корня документа с uri запроса. После этого он разделяет uri запроса на SCRIPT_NAME и PATH_INFO. Это хорошо, если у вас есть старый проект CGI или вы хотите строго следовать rfc3875.

Я также предоставил синтаксис в стиле nginx. Если вы укажете cgi pass, плагин пропустит шаг поиска CGI-скрипта. Он использует значение, которое вы предоставили, напрямую. Вы можете ссылаться на переменные nginx во втором аргументе, например: cgi pass $document_root$uri. Пример выше делает что-то похожее на rfc3875, но не равное. В этой форме uri запроса будет присвоен PATH_INFO напрямую. А SCRIPT_NAME будет пустым. Эта форма действительно хороша для динамической генерации контента. Она обходит сложное и ненужное переписывание uri.

Кроме того, вторая форма также предоставляет вам возможность передавать дополнительные аргументы скрипту, например: cgi pass my_script.sh $uri. С этим вы можете полностью избежать путаницы с переменными окружения rfc3875.

Если вы укажете off, плагин будет отключен.

По умолчанию: off

cgi_pass <script_path>

Псевдоним для cgi pass <script_path>.

cgi_interpreter [interpreter] [args...]

Устанавливает интерпретатор и аргументы интерпретатора для CGI-скрипта.

Когда эта опция не пуста, CGI-скрипт будет запущен с указанным интерпретатором. В противном случае скрипт будет выполнен напрямую.

Эта опция может содержать переменные nginx, смотрите https://nginx.org/en/docs/varindex.html для получения дополнительной информации.

Эта опция крайне полезна во многих сценариях, например:

  • запуск CGI-скриптов без прав на выполнение
  • выполнение sudo перед запуском CGI-скрипта
  • обертывание общего бинарного файла как CGI-скрипта
  • фильтрация вывода CGI-скрипта
  • ...

По умолчанию: пусто

cgi_working_dir <dir>

Устанавливает рабочий каталог для CGI-скрипта.

Если это значение установлено в пустое, CGI-скрипты унаследуют рабочий каталог nginx.

Если это значение установлено в непустую строку, CGI-скрипт будет запущен с указанным рабочим каталогом.

Действие по изменению рабочего каталога может завершиться неудачей. Например, если указанный каталог не существует, нет разрешения или имя слишком длинное. В этом случае скрипт не сможет выполниться.

Эта опция не изменяет способ поиска интерпретатора или скрипта (если они указаны с относительным путем, они всегда будут относиться к рабочему каталогу nginx).

Эта опция может содержать переменную nginx. Хотя я не знаю, для чего это нужно. Возможно, вы можете установить разные рабочие каталоги для разных server_name с помощью этого.

По умолчанию: пусто

cgi_body_only <on|off>

Стандартный CGI-скрипт должен выводить две части: заголовок и тело. И пустую строку, чтобы разделить эти две части.

Если вы хотите просто запустить обычную программу как CGI-программу, вы можете включить это.

Как только эта опция включена, весь вывод будет рассматриваться как тело ответа и отправляться клиенту.

По умолчанию: off

cgi_path <PATH>

Изменяет переменную окружения PATH для CGI-скрипта.

По умолчанию: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

cgi_strict <on|off>

Включает или отключает строгий режим.

Когда строгий режим включен, плохой заголовок cgi приведет к ошибке 500. Когда строгий режим отключен, плохой заголовок cgi будет передан как есть.

По умолчанию: on

cgi_set_var <name> <value>

Добавляет и передает дополнительные переменные окружения в CGI-скрипт. Первый аргумент этой команды — это имя переменной окружения. Она должна содержать только буквы, цифры и символ подчеркивания и не должна начинаться с цифры. Второй аргумент этой команды — это выражение значения переменной. Оно может содержать переменные nginx, смотрите https://nginx.org/en/docs/varindex.html для получения дополнительной информации.

Эта опция может появляться более одного раза для установки нескольких переменных. Если более одной опции устанавливают одну и ту же переменную, то будет работать последняя. Эти директивы унаследованы от предыдущего уровня конфигурации, если и только если на текущем уровне не определены директивы cgi_set_var.

Эта опция также может использоваться для переопределения стандартных переменных CGI. Это может быть полезно в некоторых случаях, например, для взлома старого CGI-скрипта или симуляции стандартных переменных, которые в настоящее время не поддерживаются этим плагином (таких как PATH_TRANSLATED, REMOTE_IDENT). Но это не рекомендуется, так как это может привести к путанице в вашей системе.

cgi_stderr <off|info|warn|error|crit|alert|emerg|stderr>

cgi_stderr file <path_to_file>

По умолчанию nginx-cgi захватывает вывод stderr CGI-скрипта и записывает его в журнал nginx с уровнем warn. Вы можете изменить это поведение здесь.

  • off: полностью игнорировать вывод stderr.

  • info, warn, error, crit, alert, emerg: перенаправить stderr CGI в журнал nginx с указанным уровнем. Примечание: эта опция может быть немного затратной, так как для каждого процесса CGI требуется дополнительный pipe. Если вас это беспокоит, вам следует избегать этого.

  • stderr: перенаправить stderr CGI в stderr процесса nginx.

  • file : перенаправить stderr CGI в файл.

cgi_rdns <on|off|double> [required]

Включает или отключает обратный DNS.

off: отключить функцию rdns.

on: Выполнить обратный DNS перед запуском CGI-скрипта и передать результат rdns в CGI-скрипт через переменную окружения REMOTE_HOST.

double: После обратного DNS снова выполнить прямой DNS, чтобы проверить результат rdns. Если результат совпадает, передать результат как REMOTE_HOST.

required: Если rdns не удался, клиенту будет возвращен код 403, 503 или 500. В зависимости от причины сбоя rdns.

Если вы включите эту опцию, вам также нужно настроить resolver в nginx. В противном случае вы получите ошибку no resolver defined to resolve.

замечания автора: не включайте эту опцию, это замедлит каждый запрос. Эта функция может быть легко реализована с помощью dig -x или nslookup в скрипте. Единственная причина, по которой я реализовал это, — это сделать модуль полностью совместимым со стандартом rfc3875.

cgi_timeout <t1> [t2]

Отправляет сигналы TERM/KILL процессу CGI, если он работает слишком долго.

Если и t1, и t2 равны 0. Функция таймаута отключена.

Если t1 или t2 не равны 0. Сигнал TERM или KILL будет отправлен процессу после таймаута.

Если и t1, и t2 не равны нулю. Сначала отправьте TERM в момент t1. А затем снова отправьте KILL в момент t1+t2 (если процесс все еще жив в этот момент).

Если t2 не указано, оно считается равным 0.

По умолчанию: 0 0

Стандартные переменные окружения

Nginx-cgi реализует почти все стандартные переменные rfc3875. Если они не могут покрыть все ваши потребности, вы можете добавить свою переменную с помощью cgi_set_var. Также эти переменные могут быть переопределены с помощью cgi_set_var, если вы действительно хотите.

  • AUTH_TYPE, REMOTE_USER (стандарт rfc3875)

Устарело с версии v0.15 по соображениям безопасности. Смотрите issue #22 для получения подробностей и обходных путей.

Кроме того, заголовок Authorization по умолчанию не виден в CGI-скрипте по соображениям безопасности. Если вы хотите получить доступ к заголовку авторизации в CGI-скрипте, попробуйте cgi_set_var HTTP_AUTHORIZATION $http_authorization.

  • CONTENT_LENGTH, CONTENT_TYPE (стандарт rfc3875)

То же самое, что и заголовки запроса Content-Length и Content-Type.

  • GATEWAY_INTERFACE (стандарт rfc3875)

Всегда будет CGI/1.1 в этом плагине.

  • PATH_INFO (стандарт rfc3875)

Допустим, у вас есть скрипт по адресу /cgi-bin/hello.sh, и вы обращаетесь к http://127.0.0.1/cgi-bin/hello.sh/somewhat.

Тогда PATH_INFO будет содержать строку /somewhat.

В сочетании с rewrite или cgi pass, эта переменная может использоваться для динамической генерации контента.

  • PATH_TRANSLATED (стандарт rfc3875)

Примечание: эта опция не реализована строго в соответствии с rfc3875. Пожалуйста, избегайте этого, если вы пишете новый CGI-скрипт.

Это связано с PATH_INFO.

Допустим, у вас есть скрипт по адресу /cgi-bin/hello.sh, и вы обращаетесь к http://127.0.0.1/cgi-bin/hello.sh/somewhat.

Стандарт говорит, что сервер должен снова попробовать с http://127.0.0.1/somewhat, и выяснить, куда должен быть сопоставлен uri.

По техническим причинам я просто конструирую эту переменную из корня документа и переменной PATH_INFO.

Поведение может измениться в будущих версиях.

  • QUERY_STRING (стандарт rfc3875)

Содержит строку запроса запроса. Например, если вы обращаетесь к http://127.0.0.1/cgi-bin/hello.sh?a=1&b=2, QUERY_STRING будет содержать a=1&b=2.

  • REMOTE_ADDR, (стандарт rfc3875)

IP-адрес клиента.

  • REMOTE_HOST (стандарт rfc3875)

Имя хоста клиента. Доступно только если включен cgi_rdns.

Если cgi_rdns включен, nginx-cgi выполнит обратный DNS и найдет домен, соответствующий REMOTE_ADDR. Если такой найден, он будет установлен в REMOTE_HOST.

Если cgi_rdns в режиме double, после RDNS nginx-cgi снова выполнит прямой DNS. REMOTE_HOST будет установлен только в том случае, если результат прямого DNS совпадает с исходным адресом.

Смотрите cgi_rdns для получения дополнительной информации.

  • REMOTE_IDENT (стандарт rfc3875)

Плагин nginx-cgi не поддерживает это по соображениям безопасности.

  • REQUEST_METHOD (стандарт rfc3875)

Метод запроса, например: GET, POST...

  • SCRIPT_NAME (стандарт rfc3875)

Путь к текущему скрипту. Обычно вам это не нужно. Он не содержит полного пути. Смотрите SCRIPT_FILENAME.

Единственная причина использовать это — конструировать URI после переписывания. Вы можете использовать SCRIPT_NAME + PATH_INFO, чтобы получить URI после переписывания.

  • SERVER_NAME (стандарт rfc3875)

Имя сервера, обычно оно равно заголовку Host без части порта. Если заголовок Host не появляется в запросе (HTTP/1.0) или содержит недопустимое значение, то это значение устанавливается в отраженный IP-адрес сервера. Если IP-адрес является адресом ipv6, он будет заключен в скобки, например [::1].

  • SERVER_PORT (стандарт rfc3875)

Порт, на котором слушает сервер, например 80, 443...

  • SERVER_PROTOCOL (стандарт rfc3875)

Протокол, используемый между клиентом и сервером. Например, HTTP/1.0, HTTP/1.1...

  • SERVER_SOFTWARE (стандарт rfc3875)

Содержит строку nginx и версию, например nginx/1.27.4.

  • X_ (стандарт rfc3875)

Все заголовки http-запроса с префиксом X- будут преобразованы в переменные X_. Например:

Если в заголовке появляется X-a: 123, X_A будет установлено в 123.

  • HTTP_ (стандарт rfc3875)

Все остальные заголовки http-запроса будут преобразованы в переменные HTTP_, например:

Если в заголовке появляется Accept: */*, HTTP_ACCEPT будет установлено в */*.

  • DOCUMENT_ROOT (нестандартный, имплементирован apache2)

Корень документа текущего блока location, смотрите директиву root в nginx.

  • REMOTE_PORT (нестандартный, имплементирован apache2)

Номер порта клиента.

  • REQUEST_SCHEME (нестандартный, имплементирован apache2)

http или https.

  • REQUEST_URI (нестандартный, имплементирован apache2)

Сырой uri до переписывания. Если вы хотите URL после переписывания, попробуйте SCRIPT_NAME + PATH_INFO.

Примечание: эта переменная не совпадает с переменной nginx $request_uri. Вы можете найти документ по адресу https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html.

  • SCRIPT_FILENAME (нестандартный, имплементирован apache2)

Полный путь к CGI-скрипту.

  • SERVER_ADDR (нестандартный, имплементирован apache2)

IP-адрес сервера. Если у сервера несколько IP-адресов, значение этой переменной может отличаться, если запросы поступают с разных интерфейсов.

Известные проблемы

Реализация PATH_TRANSLATED неточная

Согласно rfc3875, PATH_TRANSLATED должен указывать на файл, как если бы $PATH_INFO был доступен как uri. Но это действительно сложно реализовать на nginx, это требует повторного триггера процесса location nginx. И эти функции являются частными, к ним нельзя получить доступ напрямую из плагина. Другой способ реализации — это запуск подзапроса, но это слишком затратно, и эта переменная действительно редко используется. Это действительно не стоит делать. Поэтому я просто конструирую эту переменную из корня документа и переменных path_info.

Реализация RDNS не обращается к /etc/hosts

Реализация разрешателя nginx не обращается к /etc/hosts. Я не хочу реализовывать дополнительный разрешатель в плагине. Поэтому я просто игнорирую эту проблему.

Ссылки

rfc3875

https://datatracker.ietf.org/doc/html/rfc3875

nginx

https://nginx.org/en/docs/dev/development_guide.html https://hg.nginx.org/nginx-tests

Заголовки hop-by-hop

https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1

CGI окружения

https://datatracker.ietf.org/doc/html/rfc3875#section-4.1

Apache CGI

https://httpd.apache.org/docs/2.4/howto/cgi.html

Lighttpd CGI

https://redmine.lighttpd.net/projects/lighttpd/wiki/Mod_cgi

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-cgi.