cgi: CGI-Unterstützung für NGINX
Installation
Sie können dieses Modul in jeder RHEL-basierten Distribution installieren, einschließlich, aber nicht beschränkt auf:
- RedHat Enterprise Linux 7, 8, 9 und 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 und 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
Aktivieren Sie das Modul, indem Sie Folgendes am Anfang von /etc/nginx/nginx.conf hinzufügen:
load_module modules/ngx_http_cgi_module.so;
Dieses Dokument beschreibt nginx-module-cgi v0.15 veröffentlicht am 05. März 2026.
Bringt CGI-Unterstützung zu Nginx und Angie Webserver.
| OS | Getestet mit | Nginx | Angie |
|---|---|---|---|
| Linux | AlmaLinux 9, Debian 12 und Ubuntu 24.04/20.04 | okay | okay |
| Darwin | MacOS 15.1 | okay | okay |
| BSD | FreeBSD 14.2 und OpenBSD 7.6 | okay | okay |
| Solaris | OmniOS r1510521 | okay | okay |
| Windows | Kein Plan, nginx unterstützt Windows kaum |
Vor allem
CGI ist weder ein Dämon noch ein Engel. Es ist einfach ein Werkzeug. Genau wie ein Kochmesser in den Händen eines Kochs oder ein Schwert in den Händen eines Kriegers, werden Sie ein Schwert nicht zum Kochen verwenden, noch nehmen Sie ein Kochmesser mit ins Schlachtfeld. Das Gleiche gilt für CGI, es hat seine angemessenen Szenarien und sollte nicht missbraucht oder dämonisiert werden.
CGI ist gut für:
- Anwendungen mit niedriger Frequenz, wie Systemverwaltung
- Ressourcenlimitierte Systeme, wie eingebettete Systeme
- Niedrigbudgetprojekte, wie persönliche Websites
- Prototyping, um schnell zu iterieren
CGI ist schlecht für:
- Hohe QPS
- Hoher Verkehr
- Hohe Parallelität
Ich habe einen Discord-Kanal erstellt. Wenn:
- Sie auch ein Fan von CGI sind
- Wenn Sie ein Problem mit nginx-cgi haben
- Wenn Sie Updates zu nginx-cgi erhalten möchten
- Wenn Sie mehr Freunde kennenlernen möchten
Bitte treten Sie uns bei: https://discord.gg/EJSfqHHmaR.
Benchmark
CGI ist nicht so langsam, wie die Leute normalerweise denken. In Wirklichkeit sind seine Hauptbeschränkungen der Prozessüberhead und die Latenzvariabilität -- nicht, dass es nur ein paar Anfragen pro Minute verarbeiten kann.
Ich habe einen einfachen Test durchgeführt.
Testumgebung:
- Maschine: kostengünstige ($5/Monat) Vultr-Instanz (1 shared vCPU ~2.3GHz, 1GB RAM)
- OS: Debian 12
- Testbefehl für CGI:
ab -n 1000 -c 100 127.0.0.1/cgi-bin/hello.sh - Testbefehl für einfachen Text:
ab -n 1000 -c 100 127.0.0.1/hello.txt
Ergebnisse:
hello.sh: 1007 req/shello.txt: 4891 req/s
Schnellstart
Fügen Sie das Repository hinzu (Ubuntu-Beispiel - ersetzen Sie 'ubuntu' und 'jammy' durch Ihre Distribution)
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
Quellcode auschecken
git clone https://github.com/pjincz/nginx-cgi cd nginx-cgi
rpmbuild initialisieren
rpmdev-setuptree cd ~/rpmbuild
Spezifikationsdatei aus dem Repository herunterladen
wget https://github.com/pjincz/nginx-cgi/raw/refs/heads/main/fedora/nginx-cgi.spec
!/bin/bash
echo "Content-Type: text/plain" echo
echo "Willkommen in der CGI-Welt!"
Vergessen Sie nicht, die x-Berechtigung für das CGI-Skript hinzuzufügen:
```sh
chmod +x your-document-root-dir/cgi-bin/hello.sh
Starten Sie nginx neu und versuchen Sie es:
systemctl restart nginx
curl http://127.0.0.1/cgi-bin/hello.sh
Wenn nichts schiefgeht, finden Sie eine Begrüßung aus der CGI-Welt. :D
Verwendung
Laden des Plugins
Wenn dieses Plugin im Standardmodulpfad von nginx installiert ist (z. B. /usr/lib/nginx/modules), wird das Plugin automatisch geladen.
Andernfalls müssen Sie das Plugin manuell mit load_module laden.
Fügen Sie die folgende Anweisung zum obersten Kontext von nginx hinzu, um das Plugin zu laden:
load_module <dir-of-plugin>/ngx_http_cgi_module.so;
CGI aktivieren
Nachdem das Plugin geladen wurde, können Sie cgi on zu den Standortkontexten hinzufügen, um CGI zu aktivieren. Beispiel:
location /cgi-bin {
cgi on;
}
Sobald CGI an einem Standort aktiviert ist, wird CGI auch für alle verschachtelten Standorte aktiviert. Wenn Sie CGI für einen untergeordneten Standort deaktivieren möchten, verwenden Sie einfach cgi off.
Wenn der Standort aufgerufen wird, sucht nginx-cgi das Skript im Dokumentenstamm (dies wird durch die root-Anweisung festgelegt). Wenn Sie beispielsweise den Dokumentenstamm als /var/www/html festgelegt haben, wird beim Zugriff auf /cgi-bin/hello.sh das Skript /var/www/html/cgi-bin/hello.sh ausgeführt.
Nginx-cgi unterstützt auch alias, es ist wie die root-Anweisung in nginx, der einzige Unterschied besteht darin, dass das Standortpräfix aus der URI entfernt wird. Wenn Sie beispielsweise möchten, dass /cgi/hello.sh auf dasselbe Skript verweist, können Sie Folgendes tun:
location /cgi {
alias /var/www/html/cgi-bin;
cgi on;
}
Hello-Skript
Ein CGI-Skript kann in jeder Sprache geschrieben werden. Hier ist ein Beispiel mit Shell. Sie können es zum Testen in /var/www/html/cgi-bin/hello.sh speichern (wenn Sie den Standard-Dokumentenstamm nicht geändert haben):
#!/bin/sh
echo "Status: 200 OK"
echo "Content-Type: text/plain"
echo
echo "Hallo Welt"
Die erste Zeile des Skripts ist ein Shebang. Wenn Sie cgi_interpreter klar festgelegt haben, ist es in Ordnung, diese Zeile zu entfernen, andernfalls führt das Fehlen des Shebang zu einem 500-Fehler. Einige Shells erlauben es, dass Skripte auch ohne Shebang ausführbar sind, aber das ist hier nicht erlaubt. Wenn ein Skript von der Shell ausführbar ist, aber einen 500-Fehler zurückgibt, überprüfen Sie den Shebang.
Die Ausgabe des CGI-Skripts besteht aus 2 Abschnitten: dem Header-Abschnitt und dem Body-Abschnitt. Die ersten 2 echo-Anweisungen geben den Header-Abschnitt aus, und die letzte echo-Anweisung gibt den Body-Abschnitt aus. Die echo-Anweisung in der Mitte gibt den Separator aus. Sowohl der Header- als auch der Body-Abschnitt können leer sein, aber der Separator ist obligatorisch. Das Fehlen des Separators führt zu einem 500-Fehler.
Alle Zeilen im Header-Abschnitt werden als normale HTTP-Antwortheaderzeilen geparst.
Und dann an die Client-Seite weitergegeben. Es gibt einen speziellen Header Status, der in der Antwortstatuszeile weitergegeben wird. Wenn cgi_strict aktiviert ist, überprüft nginx-cgi alle CGI-Ausgabeheader, und ein 500-Fehler wird zurückgegeben, wenn ungültige Header gefunden werden. Andernfalls werden ungültige Header ebenfalls an die Client-Seite weitergeleitet. Es wird dringend empfohlen, cgi_strict aktiviert zu lassen.
Nach dem Separator wird die gesamte Ausgabe als Body an den Client gesendet.
x-Berechtigung
Letztendlich müssen Sie die x-Berechtigung für die Datei hinzufügen:
chmod +x /var/www/html/cgi-bin/hello.sh
Normalerweise benötigen Sie die x-Berechtigung, um das Skript ausführbar zu machen. Das Fehlen der x-Berechtigung kann einen 403-Fehler verursachen. Wenn Sie dies aus irgendeinem Grund nicht tun können, kann cgi_interpreter helfen.
Anfrageheader
Anfrageheader werden geparst und dann in Umgebungsvariablen übersetzt und an das CGI-Skript übergeben.
Zum Beispiel können Sie die Abfragezeichenfolge in der Umgebungsvariablen QUERY_STRING finden.
Und auf Http-Accept über HTTP_ACCPET zugreifen.
Hier ist ein Beispiel:
#!/bin/sh
echo ""
echo "Abfragezeichenfolge: $QUERY_STRING"
echo "HTTP akzeptieren: $HTTP_ACCEPT"
Für die vollständige Liste der Umgebungsvariablen siehe Abschnitt Umgebungen.
Anfrage-Body
Der Anfrage-Body wird über stdin übergeben. Hier ist ein Beispiel, um den gesamten Anfrage-Body zu lesen und auszugeben:
#!/bin/sh
echo ""
body=$(cat)
echo "Anfrage-Body: $body"
Streaming
Nginx-cgi unterstützt Streaming für sowohl Anfrage- als auch Antwort-Body. Zum Beispiel können wir einen einfachsten Online-Rechner mit bc implementieren:
#!/bin/sh
echo ""
bc 2>&1
Dann können wir unseren Rechner mit curl testen:
curl 127.0.0.1/cgi-bin/bc.sh --no-progress-meter -T .
Das nginx-cgi-Plugin ist schlau genug, um den richtigen Weg zu wählen, um den Anfrage-Body zurückzugeben. Wenn es alle Ausgaben schnell genug erhält, wird der Body auf einmal ausgegeben. Wenn die Ausgabe verzögert wird, wird der Body stückweise (HTTP 1.1) oder streamend (HTTP 1.0) ausgegeben.
Hop-by-hop HTTP-Header
Hop-by-hop HTTP-Header sind in der Ausgabe des CGI-Skripts nicht erlaubt. Wenn sie hier in der Antwort erscheinen, wird ein 500-Fehler an den Client zurückgegeben.
Für weitere Informationen: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#hop-by-hop_headers
Tricks && FAQ
Ich möchte alle Umgebungsvariablen auflisten
Legen Sie das folgende Skript in Ihr CGI-Verzeichnis und rufen Sie es von Ihrem Terminal aus auf:
#!/bin/sh
echo 'Content-Type: text/plain'
echo
printenv
Ich möchte Root-Rechte
Legen Sie eine Sudo-Datei in /etc/sudoers.d ab und führen Sie sudo in Ihrem Skript aus oder setzen Sie cgi_interpreter auf /usr/bin/sudo.
Hier ist ein Beispiel für eine Sudo-Konfigurationsdatei:
# Erlaube www-data, /var/www/bin/my-danger-script mit Root-Konto auszuführen
www-data ALL=(root) NOPASSWD: /var/www/bin/my-danger-script
# Erlaube, dass alle CGI-Skripte direkt mit sudo von nginx-cgi gestartet werden
www-data ALL=(root) NOPASSWD: SETENV: /var/www/html/cgi-bin/*
Wie kann ich CGI-Skripte mit chroot ausführen
Es wird dringend davon abgeraten, CGI-Skripte mit chroot auszuführen. Denn chroot ist nicht für Sicherheitszwecke konzipiert. Es teilt immer noch viele Kernel-Ressourcen mit dem Host-System. Wenn Sie beispielsweise ps -ef im chroot-Prozess ausführen, werden alle Prozesse im Host-System zurückgegeben. Das sollte nicht zu schrecklich sein? Nein, das ist wirklich schrecklich, denn Sie können aus dem gleichen Grund auch kill im chroot-Skript ausführen. Und die Leute führen normalerweise Programme mit Root-Rechten in einer chroot-Umgebung aus. Das ist furchtbar schlecht. Es bringt das System in ein höheres Risiko, als einfach das Skript mit www-data auszuführen.
Wenn Sie eine Sandbox-Umgebung möchten, sind lxc, docker und jails viel besser für diesen Zweck.
Wenn Sie trotzdem chroot verwenden möchten, lassen Sie mich Ihnen zeigen, wie es geht.
In diesem Beispiel gehe ich davon aus, dass Sie /var/www/html als Dokumentenstamm verwenden.
Bereiten Sie zunächst ein CGI-Skript vor:
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 "Dateien unter /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh
# versuchen Sie es
/var/www/html/cgi-bin/ls.sh
Schritt 1: Bereiten Sie ein chroot-Verzeichnis vor.
Es gibt viele Möglichkeiten, diesen Schritt auszuführen. debootstrap ist eine beliebte Methode auf Debian-basierten Systemen. busybox ist die leichteste Methode. docker ist eine moderne Methode.
Lassen Sie uns hier ein leichtes Verzeichnis mit busybox erstellen:
# In diesem Beispiel lege ich alles in /var/www/chroot
# Seien Sie vorsichtig, ich lade hier die x86_64 busybox-Version herunter, Sie müssen sie möglicherweise ändern
# Sie benötigen Root-Rechte, um alle folgenden Befehle auszuführen, ich bin zu faul, um
# sudo vor jeden Befehl zu setzen.
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
# versuchen Sie es
chroot "$root_dir" ls
Schritt 2: Binden Sie den Dokumentenstamm in das chroot-Verzeichnis ein
mkdir -p /var/www/chroot/var/www/html
mount --bind /var/www/html /var/www/chroot/var/www/html
# versuchen Sie es
ls /var/www/chroot/var/www/html
Hinweis:
-
Ich verwende hier einen Trick, nach dem chroot bleibt der Dokumentenstamm derselbe. Damit können wir Zeit bei der Pfadzuordnung sparen.
-
Das Mounten wird nach einem Neustart nicht bestehen bleiben. Möglicherweise müssen Sie einen Eintrag in /etc/fstab hinzufügen. Oder verschieben Sie /var/www/html in chroot und erstellen Sie einen symbolischen Link außerhalb.
Schritt 3: Erlauben Sie www-data, chroot mit Root-Rechten auszuführen.
cat >/etc/sudoers.d/www-run-with-chroot <<EOF
# Erlaube und erlaube nur www-data, chroot mit /var/www/chroot auszuführen
www-data ALL=(root) NOPASSWD: /usr/sbin/chroot /var/www/chroot *
EOF
Jetzt ist alles bereit, fügen Sie den folgenden Abschnitt zu Ihrem nginx/angie hinzu:
location /cgi-bin {
cgi on;
cgi_interpreter /usr/bin/sudo /usr/sbin/chroot /var/www/chroot;
}
versuchen Sie es:
curl 127.0.0.1/cgi-bin/ls.sh
Wie kann ich CGI-Skripte mit Docker ausführen
In diesem Beispiel gehe ich davon aus, dass Sie /var/www/html als Dokumentenstamm verwenden.
Bereiten Sie zunächst ein CGI-Skript vor:
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 "Dateien unter /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh
# versuchen Sie es
/var/www/html/cgi-bin/ls.sh
Erstellen Sie einen Container und lassen Sie ihn im Hintergrund laufen:
# Ändern Sie -v bei Bedarf
# -d: läuft im Hintergrund
# -i -t: hält ein Terminal
# --restart always: hält den Container am Leben
docker run -dit --restart always --name my_cgi_docker -v /var/www:/var/www busybox sh
# versuchen Sie es
docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh
Erlauben Sie www-data, docker-Befehle auszuführen:
sudo usermod -aG docker www-data
# versuchen Sie es
sudo -u www-data docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh
Jetzt ist alles bereit, fügen Sie den folgenden Abschnitt zu Ihrem nginx/angie hinzu:
location /cgi-bin {
cgi on;
cgi_interpreter /usr/bin/docker exec my_cgi_docker;
}
Wie kann ich CGI-Skripte mit Jails ausführen
Okay, sind Sie ein Fan von FreeBSD? Ich auch.
Es ist wirklich ähnlich wie das Ausführen von Skripten mit chroot.
Hier gehe ich ebenfalls davon aus, dass Sie /var/www/html als Dokumentenstamm verwenden.
Bereiten Sie zunächst ein CGI-Skript vor:
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 "Dateien unter /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh
# versuchen Sie es
/var/www/html/cgi-bin/ls.sh
Schritt 1: Erstellen Sie ein Jail
Lassen Sie uns das Jail in /var/www/jail ablegen.
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 .
# Erstellen Sie Einhängepunkte
mkdir -p /var/www/jail/var/www/html
touch /var/www/jail/etc/resolv.conf
Legen Sie die folgende Konfiguration in /etc/jail.conf ab:
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";
# mount /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;
# Kommentieren Sie die folgenden Zeilen aus, wenn Sie Netzwerkzugriff im Jail erlauben möchten
# ip4 = inherit;
# ip6 = inherit;
# exec.prestart += "mount_nullfs /etc/resolv.conf /var/www/jail/etc/resolv.conf || true";
# Kommentieren Sie die folgenden Zeilen aus, wenn Sie auch `ping` im Jail verfügbar machen möchten
# allow.raw_sockets = 1;
persist; # Jail behalten, wenn kein Prozess läuft
}
Und stellen Sie sicher, dass die folgende Zeile in /etc/rc.conf erscheint:
jail_enable="YES"
Und starten Sie das Jail:
service jail start www-jail
# versuchen Sie es
jexec www-jail ls /
jexec www-jail /var/www/html/cgi-bin/ls.sh
Schritt 2: Erlauben Sie www, jexec mit Root-Rechten auszuführen.
Ich verwende hier sudo. Ich bin mit doas nicht vertraut, wenn Sie doas bevorzugen, können Sie es selbst ausprobieren. Jedenfalls sind weder sudo noch `doas standardmäßig in FreeBSD vorinstalliert. Sie müssen eines von beiden manuell installieren.
cat >/usr/local/etc/sudoers.d/www-jexec <<EOF
# Erlaube und erlaube nur `www`, `jexec` mit `www-jail` auszuführen
www ALL=(root) NOPASSWD: /usr/sbin/jexec www-jail *
EOF
# versuchen Sie es
sudo -u www sudo jexec www-jail /var/www/html/cgi-bin/ls.sh
Jetzt ist alles bereit, fügen Sie den folgenden Abschnitt zu Ihrem nginx/angie hinzu:
location /cgi-bin {
cgi on;
cgi_interpreter /usr/local/bin/sudo /usr/sbin/jexec www-jail;
}
versuchen Sie es:
curl 127.0.0.1/cgi-bin/ls.sh
Ich möchte einen langlaufenden Hintergrundprozess erstellen
Stellen Sie einfach sicher, dass Sie stdout nicht erben, wenn Sie den Prozess erstellen (idealerweise vermeiden Sie auch das Erben von stdin und stderr). Hier ist ein Beispiel, das in Shell geschrieben ist.
taskid=1234
logfile="/var/lib/my-project/$taskid"
./long-run-task.sh "$taskid" </dev/null >"$logfile" 2>&1 &
Oder wenn Sie mit Pipe-Operationen vertraut sind, schließen Sie einfach stdout (es ist auch besser, stdin und stderr zu schließen), die HTTP-Anfrage wird sofort abgeschlossen. Und Sie können den Prozess als Hintergrundprozess verwenden.
exec </dev/null >somewhere 2>&1
# jetzt ist die HTTP-Antwort abgeschlossen, tun Sie, was immer Sie möchten
sleep 9999
Meine HTTP-Anfrage hängt
Wie Sie oben sehen. Im CGI-Umfeld hängt der Lebenszyklus der HTTP-Anfrage vom Lebenszyklus der Pipe (stdout) ab.
Jeder Kindprozess könnte die Pipe des CGI-Prozesses erben. Wenn ein Prozess, der stdout geerbt hat, aktiv bleibt, wird die HTTP-Anfrage niemals abgeschlossen.
Dies kann verwirrend sein, wenn Sie einen langlaufenden Hintergrundprozess erstellen oder den CGI-Prozess beenden möchten.
Um einen langlaufenden Prozess zu erstellen, siehe das obige Thema.
Um den CGI-Prozess zu beenden, beenden Sie die gesamte Prozessgruppe anstelle des CGI-Prozesses selbst.
cgi_pid=...
# tun Sie dies nicht
# kill "$cgi_pid"
# tun Sie dies
kill -- "-$cgi_pid"
Ich möchte mein CGI-Skript beenden
Siehe das obige Thema.
Ich möchte Inhalte dynamisch generieren
Traditionell verwenden die Leute Umschreibungen, um dies zu erreichen. Aber hier ist es viel einfacher.
Sie können es mit cgi pass tun. Hier ist ein Beispiel, um Markdown dynamisch zu rendern:
{
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>"
Ich mag keine Suffixe in der URL
Weg 1: Entfernen Sie das Suffix des CGI-Skripts
Weg 2: Umschreibung durchführen
Weg 3: cgi pass
Wie kann ich einen Status zurückgeben, der nicht 200 ist
#!/bin/sh
echo "Status: 404"
echo "Content-Type: text/plain"
echo
echo "Willkommen in der Leere"
Wie kann ich eine Umleitung zurückgeben
#!/bin/sh
echo "Status: 302"
echo "Location: https://theuselessweb.com"
echo
Wie kann ich den HTTP-Anfrage-Body erhalten
Sie können den Anfrage-Body von stdin lesen. Wenn Sie Shell verwenden, kann cat den Anfrage-Body schnell in eine Datei speichern.
Wie kann ich eine Datei an den Client senden
Für kleine Dateien können Sie die Datei direkt an stdout schreiben.
Für große Dateien ist es viel besser, eine 302-Antwort zu senden. Da die CGI-Antwort streamend ist, kann das Protokoll nicht einfach Caching, chunked Downloads oder Resume-Unterstützung handhaben.
Ich möchte CGI mit Python, Ruby, Perl, C, C++... schreiben
Nur zu. Nginx-cgi kümmert sich nicht darum, welche Sprache Sie verwenden. Es greift einfach auf Informationen aus der Umgebungsvariablen zu, liest den Anfrage-Body von stdin und schreibt die Ausgabe an stdout.
Handbuch
Optionen
cgi <on|off> oder cgi pass <script_path> [script_args...]
Aktivieren oder deaktivieren Sie das CGI-Modul im angegebenen Standortblock.
Wenn Sie hier on angeben, funktioniert das Plugin im traditionellen Modus. Es analysiert zuerst die Anfrage-URI und lokalisiert dann das Skript im Dokumentenstammverzeichnis mit der Anfrage-URI. Danach trennt es die Anfrage-URI in SCRIPT_NAME und PATH_INFO. Dies ist gut, wenn Sie ein altes CGI-Projekt haben oder die RFC3875 strikt einhalten möchten.
Ich habe hier auch eine nginx-ähnliche Syntax bereitgestellt. Wenn Sie hier cgi pass angeben, überspringt das Plugin den Schritt zur Lokalisierung des CGI-Skripts. Es verwendet den von Ihnen angegebenen Wert direkt. Sie können nginx-Variablen im zweiten Argument referenzieren, z. B.: cgi pass $document_root$uri. Das obige Beispiel tut etwas Ähnliches wie RFC3875, ist aber nicht gleich. In dieser Form wird die Anfrage-URI direkt PATH_INFO zugewiesen. Und SCRIPT_NAME bleibt leer. Diese Form ist wirklich gut für die dynamische Inhaltserzeugung. Sie umgeht die komplexe und unnötige URI-Umschreibung.
Darüber hinaus bietet die zweite Form auch die Möglichkeit, zusätzliche Argumente an das Skript zu übergeben, z. B.: cgi pass my_script.sh $uri. Damit können Sie die verwirrenden RFC3875-Umgebungsvariablen vollständig vermeiden.
Wenn Sie hier off angeben, wird das Plugin deaktiviert.
Standard: off
cgi_pass <script_path>
Alias von cgi pass <script_path>.
cgi_interpreter [interpreter] [args...]
Setzen Sie den Interpreter und die Interpreterargumente für das CGI-Skript.
Wenn diese Option nicht leer ist, wird das CGI-Skript mit dem angegebenen Interpreter ausgeführt. Andernfalls wird das Skript direkt ausgeführt.
Diese Option kann nginx-Variablen enthalten, siehe https://nginx.org/en/docs/varindex.html für weitere Details.
Diese Option ist in vielen Szenarien äußerst nützlich, zum Beispiel:
- Ausführen von CGI-Skripten ohne x-Berechtigung
- Führen Sie sudo aus, bevor Sie das CGI-Skript ausführen
- Allgemeine Binärdateien als CGI-Skript einwickeln
- Ausgabe von CGI-Skripten filtern
- ...
Standard: leer
cgi_working_dir <dir>
Setzen Sie das Arbeitsverzeichnis des CGI-Skripts.
Wenn dieser Wert leer ist, erben CGI-Skripte das Arbeitsverzeichnis von nginx.
Wenn dieser Wert auf eine nicht leere Zeichenfolge gesetzt ist, wird das CGI-Skript mit dem angegebenen Arbeitsverzeichnis gestartet.
Die Aktion zum Ändern des Arbeitsverzeichnisses kann fehlschlagen. Beispielsweise, wenn das angegebene Verzeichnis nicht existiert, keine Berechtigung hat oder der Name zu lang ist. In diesem Fall kann das Skript nicht ausgeführt werden.
Diese Option ändert nicht die Art und Weise, wie der Interpreter oder das Skript gefunden wird (wenn sie mit einem relativen Pfad angegeben sind, beziehen sie sich immer auf das Arbeitsverzeichnis von nginx).
Diese Option kann nginx-Variablen enthalten. Obwohl ich nicht weiß, wozu dies nützlich ist. Vielleicht können Sie damit unterschiedliche Arbeitsverzeichnisse für unterschiedliche server_name einrichten.
Standard: leer
cgi_body_only <on|off>
Ein standardmäßiges CGI-Skript sollte zwei Teile ausgeben: Header und Body. Und eine leere Zeile, um diese beiden Teile zu trennen.
Wenn Sie einfach ein normales Programm als CGI-Programm ausführen möchten, können Sie dies aktivieren.
Sobald diese Option aktiviert ist, wird die gesamte Ausgabe als Antwort-Body behandelt und an den Client gesendet.
Standard: off
cgi_path <PATH>
Ändern Sie die cgi-Skript-PATH-Umgebungsvariable.
Standard: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cgi_strict <on|off>
Aktivieren oder deaktivieren Sie den strengen Modus.
Wenn der strenge Modus aktiviert ist, führt ein ungültiger CGI-Header zu einem 500-Fehler. Wenn der strenge Modus deaktiviert ist, werden ungültige CGI-Header so weitergeleitet, wie sie sind.
Standard: on
cgi_set_var <name> <value>
Fügen Sie zusätzliche Umgebungsvariablen zum CGI-Skript hinzu und übergeben Sie sie. Das erste Argument dieses Befehls ist der Name der Umgebungsvariablen. Es sollte nur Buchstaben, Zahlen und Unterstriche enthalten und darf nicht mit einer Zahl beginnen. Das zweite Argument dieses Befehls ist der Ausdruck des Wertes der Variablen. Es kann nginx-Variablen enthalten, siehe https://nginx.org/en/docs/varindex.html für weitere Details.
Diese Option kann mehr als einmal erscheinen, um mehrere Variablen festzulegen. Wenn mehr als eine Option dieselbe Variable festlegt, funktioniert die letzte. Diese Direktiven werden vom vorherigen Konfigurationslevel geerbt, wenn und nur wenn keine cgi_set_var-Direktiven auf dem aktuellen Level definiert sind.
Diese Option kann auch verwendet werden, um Standard-CGI-Variablen zu überschreiben. Dies kann in einigen Fällen nützlich sein, z. B. beim Hacken alter CGI-Skripte oder beim Simulieren von Standardvariablen, die von diesem Plugin derzeit nicht unterstützt werden (wie PATH_TRANSLATED, REMOTE_IDENT). Aber es wird nicht empfohlen, da es verwirrende Probleme in Ihr System einführen kann.
cgi_stderr <off|info|warn|error|crit|alert|emerg|stderr>
cgi_stderr file <path_to_file>
Standardmäßig erfasst nginx-cgi die stderr-Ausgabe des CGI-Skripts und leitet sie mit warn-Level in das nginx-Log weiter.
Sie können das Verhalten hier ändern.
-
off: vollständig die stderr-Ausgabe verwerfen.
-
info, warn, error, crit, alert, emerg: leiten Sie die CGI-stderr an das nginx-Log mit dem angegebenen Level weiter. Hinweis: Diese Option könnte etwas teuer sein, da sie eine zusätzliche Pipe für jeden CGI-Prozess benötigt. Wenn Ihnen dies wichtig ist, sollten Sie dies vermeiden.
-
stderr: leiten Sie die CGI-stderr an stderr des nginx-Prozesses weiter.
-
file
: leiten Sie die CGI-stderr an eine Datei weiter.
cgi_rdns <on|off|double> [required]
Aktivieren oder deaktivieren Sie die umgekehrte DNS.
off: Deaktivieren Sie die rdns-Funktion.
on: Führen Sie eine umgekehrte DNS-Abfrage durch, bevor Sie das CGI-Skript starten, und übergeben Sie das Ergebnis der umgekehrten DNS-Abfrage an das CGI-Skript über die Umgebungsvariable REMOTE_HOST.
double: Führen Sie nach der umgekehrten DNS-Abfrage erneut eine Vorwärts-DNS-Abfrage durch, um das Ergebnis der umgekehrten DNS-Abfrage zu überprüfen. Wenn das Ergebnis übereinstimmt, übergeben Sie das Ergebnis als REMOTE_HOST.
required: Wenn die umgekehrte DNS-Abfrage fehlschlägt, werden 403, 503 oder 500 an den Client zurückgegeben. Hängt vom Grund für das Fehlschlagen der umgekehrten DNS-Abfrage ab.
Wenn Sie diese Option aktivieren, müssen Sie auch einen resolver in nginx einrichten.
Andernfalls erhalten Sie einen Fehler von no resolver defined to resolve.
Hinweis des Autors: Aktivieren Sie diese Option nicht, sie macht jede Anfrage langsamer.
Diese Funktion kann leicht mit dig -x oder nslookup im Skript implementiert werden. Der einzige Grund, warum ich dies implementiere, ist, um das Modul vollständig konform mit dem RFC3875-Standard zu machen.
cgi_timeout <t1> [t2]
Senden Sie TERM/KILL-Signale an den CGI-Prozess, wenn er zu lange läuft.
Wenn sowohl t1 als auch t2 gleich 0 sind, ist die Timeout-Funktion deaktiviert.
Wenn t1 oder t2 ungleich 0 ist, wird ein TERM- oder KILL-Signal nach dem Timeout an den Prozess gesendet.
Wenn sowohl t1 als auch t2 ungleich null sind, wird zuerst TERM zum Zeitstempel t1 gesendet. Und erneut KILL zum Zeitstempel t1+t2 gesendet (wenn der Prozess zu diesem Zeitpunkt noch aktiv ist).
Wenn t2 nicht vorhanden ist, wird es als 0 behandelt.
Standard: 0 0
Standard-Umgebungsvariablen
Nginx-cgi implementiert fast alle RFC3875-Standardvariablen. Wenn sie nicht alle Ihre Anforderungen abdecken, können Sie Ihre eigene Variable mit cgi_set_var hinzufügen. Diese Variablen können auch von cgi_set_var überschrieben werden, wenn Sie dies wirklich möchten.
AUTH_TYPE,REMOTE_USER(RFC3875-Standard)
Seit v0.15 aus Sicherheitsgründen veraltet. Siehe [Issue
22](https://github.com/pjincz/nginx-cgi/issues/22) für Details und Workarounds.
Außerdem ist der Authorization-Header standardmäßig im CGI-Skript aus Sicherheitsgründen nicht sichtbar. Wenn Sie auf den Autorisierungsheader im CGI-Skript zugreifen möchten, versuchen Sie cgi_set_var HTTP_AUTHORIZATION $http_authorization.
CONTENT_LENGTH,CONTENT_TYPE(RFC3875-Standard)
Entspricht den Anfrageheadern Content-Length und Content-Type.
GATEWAY_INTERFACE(RFC3875-Standard)
Immer CGI/1.1 in diesem Plugin.
PATH_INFO(RFC3875-Standard)
Angenommen, Sie haben ein Skript unter /cgi-bin/hello.sh, und Sie greifen auf http://127.0.0.1/cgi-bin/hello.sh/somewhat zu.
Dann enthält PATH_INFO die Zeichenfolge /somewhat.
In Kombination mit URL rewrite oder cgi pass kann diese Variable zur dynamischen Inhaltserzeugung verwendet werden.
PATH_TRANSLATED(RFC3875-Standard)
Hinweis: Diese Option ist nicht strikt konform mit RFC3875 implementiert. Bitte vermeiden Sie dies, wenn Sie ein neues CGI-Skript schreiben.
Dies steht in Zusammenhang mit PATH_INFO.
Angenommen, Sie haben ein Skript unter /cgi-bin/hello.sh, und Sie greifen auf http://127.0.0.1/cgi-bin/hello.sh/somewhat zu.
Der Standard besagt, dass der Server es erneut mit http://127.0.0.1/somewhat versuchen sollte und herausfinden sollte, wo die URI abgebildet werden sollte.
Aus technischen Gründen konstruiere ich diese Variable einfach aus dem Dokumentenstamm und PATH_INFO.
Das Verhalten kann in zukünftigen Versionen geändert werden.
QUERY_STRING(RFC3875-Standard)
Enthält die Abfragezeichenfolge der Anfrage. Wenn Sie beispielsweise auf http://127.0.0.1/cgi-bin/hello.sh?a=1&b=2 zugreifen, enthält QUERY_STRING a=1&b=2.
REMOTE_ADDR, (RFC3875-Standard)
IP-Adresse des Clients.
REMOTE_HOST(RFC3875-Standard)
Hostname des Clients. Nur verfügbar, wenn cgi_rdns aktiviert ist.
Wenn cgi_rdns aktiviert ist, führt nginx-cgi eine umgekehrte DNS-Abfrage durch und findet eine Domain, die mit REMOTE_ADDR übereinstimmt. Wenn eine gefunden wird, wird sie auf REMOTE_HOST gesetzt.
Wenn cgi_rdns doppelt ist, führt nginx-cgi nach der RDNS erneut eine Vorwärts-DNS-Abfrage durch. REMOTE_HOST wird nur gesetzt, wenn das Ergebnis der Vorwärts-DNS-Abfrage mit der ursprünglichen Adresse übereinstimmt.
Siehe cgi_rdns für weitere Informationen.
REMOTE_IDENT(RFC3875-Standard)
Das nginx-cgi-Plugin unterstützt dies aus Sicherheitsgründen nicht.
REQUEST_METHOD(RFC3875-Standard)
Anfragemethode der Anfrage, z. B.: GET, POST...
SCRIPT_NAME(RFC3875-Standard)
Pfad zum aktuellen Skript. Normalerweise benötigen Sie dies nicht. Es enthält nicht den vollständigen Pfad. Siehe SCRIPT_FILENAME.
Der einzige Grund, dies zu verwenden, besteht darin, die URI nach der Umschreibung zu konstruieren. Sie können SCRIPT_NAME + PATH_INFO verwenden, um die URI nach der Umschreibung zu erhalten.
SERVER_NAME(RFC3875-Standard)
Servername, normalerweise entspricht er dem Host-Header ohne den Portteil. Wenn der Host-Header nicht in der Anfrage erscheint (HTTP/1.0) oder einen ungültigen Wert enthält, wird dieser Wert auf die reflektierte IP-Adresse des Servers gesetzt. Wenn die IP-Adresse eine IPv6-Adresse ist, wird sie in Klammern gesetzt, z. B. [::1].
SERVER_PORT(RFC3875-Standard)
Serverport, z. B. 80, 443...
SERVER_PROTOCOL(RFC3875-Standard)
Das Protokoll, das zwischen Client und Server verwendet wird. Zum Beispiel HTTP/1.0, HTTP/1.1...
SERVER_SOFTWARE(RFC3875-Standard)
Enthält eine Zeichenfolge von nginx und Version, z. B. nginx/1.27.4.
X_(RFC3875-Standard)
Alle mit X- beginnenden HTTP-Anfrageheader werden in X_-Variablen umgewandelt. Zum Beispiel:
Wenn X-a: 123 im Header erscheint, wird X_A auf 123 gesetzt.
HTTP_(RFC3875-Standard)
Alle anderen HTTP-Anfrageheader werden in HTTP_-Variablen umgewandelt, zum Beispiel:
Wenn Accept: */* im Header erscheint, wird HTTP_ACCEPT auf */* gesetzt.
DOCUMENT_ROOT(nicht standardmäßig, implementiert von Apache2)
Dokumentenstamm des aktuellen Standortblocks, siehe root-Anweisung in nginx.
REMOTE_PORT(nicht standardmäßig, implementiert von Apache2)
Portnummer des Clients.
REQUEST_SCHEME(nicht standardmäßig, implementiert von Apache2)
http oder https.
REQUEST_URI(nicht standardmäßig, implementiert von Apache2)
Die rohe URI vor der Umschreibung. Wenn Sie die URL nach der Umschreibung möchten, versuchen Sie SCRIPT_NAME + PATH_INFO.
Hinweis: Diese Variable ist nicht identisch mit der nginx-Variablen $request_uri. Sie können das Dokument unter https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html finden.
SCRIPT_FILENAME(nicht standardmäßig, implementiert von Apache2)
Der vollständige Pfad zum CGI-Skript.
SERVER_ADDR(nicht standardmäßig, implementiert von Apache2)
IP-Adresse des Servers. Wenn der Server mehrere IP-Adressen hat, kann der Wert dieser Variablen unterschiedlich sein, wenn die Anfragen von verschiedenen Schnittstellen kommen.
Bekannte Probleme
PATH_TRANSLATED-Implementierung nicht genau
Laut RFC3875 sollte PATH_TRANSLATED auf die Datei zeigen, die so behandelt wird, als ob $PATH_INFO als uri zugegriffen wurde. Aber das ist wirklich schwer in nginx zu implementieren, es erfordert, dass der nginx-Standortprozess erneut ausgelöst wird. Und diese Funktionen sind privat und können vom Plugin nicht direkt aufgerufen werden. Eine andere Möglichkeit, dies zu implementieren, besteht darin, eine Unteranfrage zu starten, aber das ist zu teuer, und diese Variable wird wirklich selten verwendet. Es ist wirklich nicht wert, es zu tun. Daher konstruiere ich diese Variable einfach aus dem Dokumentenstamm und den path_info-Variablen.
RDNS-Implementierung greift nicht auf /etc/hosts zu
Die Implementierung des Nginx-Resolvers greift nicht auf /etc/hosts zu. Ich möchte keinen zusätzlichen Resolver im Plugin implementieren. Daher ignoriere ich dieses Problem einfach.
Referenz
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-Header
https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1
CGI-Umgebungen
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
Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-cgi.