跳转至

cgi: NGINX 的 CGI 支持

安装

您可以在任何基于 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.14.1,于 2025 年 9 月 15 日发布。


NginxAngie Web 服务器带来了 CGI 支持。

操作系统 测试版本 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

快速开始(适用于 Debian 12+、Ubuntu 24.04+)

构建并安装:

## 检出源代码
git clone https://github.com/pjincz/nginx-cgi
cd nginx-cgi

#!/bin/bash

echo "Content-Type: text/plain"
echo

echo Hello CGI

为 CGI 脚本添加可执行权限:

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

现在,尝试一下:

curl http://127.0.0.1/cgi-bin/hello.sh

如果没有错误,您将获得 Hello CGI 的输出。

使用

加载插件

如果此插件安装在 nginx 的默认模块路径(例如 /usr/lib/nginx/modules),则插件将自动加载。否则,您需要通过 load_module 手动加载插件。

将以下语句添加到 nginx 的顶层上下文以加载插件:

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

启用 cgi

加载插件后,您可以在位置上下文中添加 cgi on 来启用 cgi。示例:

location /cgi-bin {
    cgi on;
}

一旦在某个位置启用了 cgi,所有嵌套位置也将启用 cgi。如果您想禁用子位置的 cgi,只需使用 cgi off

当访问该位置时,nginx-cgi 将在文档根目录下查找脚本(由 root 语句指定)。例如,如果您将文档根目录指定为 /var/www/html,那么当您访问 /cgi-bin/hello.sh 时,将执行 /var/www/html/cgi-bin/hello.sh

Nginx-cgi 还支持 alias,它类似于 nginx 中的 root 语句,唯一的区别是位置前缀将从 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 "Hello world"

脚本的第一行是 shebang。如果您明确设置了 cgi_interpreter,可以删除此行,否则缺少 shebang 将导致 500 错误。一些 shell 允许脚本在没有 shebang 的情况下可执行,但在这里不允许。如果脚本可以由 shell 运行,但返回 500 错误,请检查 shebang。

CGI 脚本的输出包含两个部分:头部部分和主体部分。前两条 echo 语句输出头部部分,最后一条 echo 语句输出主体部分。中间的 echo 语句输出分隔符。头部部分和主体部分都可以为空,但分隔符是必需的。缺少分隔符将导致 500 错误。

头部部分的所有行将被解析为普通的 HTTP 响应头行。然后传递给客户端。有一个特殊的头部 Status,它将被传递到响应状态行。如果 cgi_strict 开启,nginx-cgi 将检查所有 CGI 输出头部,如果发现无效头部,将返回 500 错误。否则,无效头部也将转发给客户端。强烈建议保持 cgi_strict 开启。

在分隔符之后,所有输出将作为主体直接发送给客户端。

x 权限

最后,您需要为文件添加可执行权限:

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

通常,您需要可执行权限才能使脚本可运行。缺少可执行权限可能导致 403 错误。如果出于某种原因无法执行此操作,cgi_interpreter 可以提供帮助。

请求头

请求头将被解析并转换为环境变量,然后传递给 CGI 脚本。

例如,您可以在 QUERY_STRING 环境变量中找到查询字符串。并通过 HTTP_ACCEPT 访问 Http-Accept

以下是一个示例:

#!/bin/sh
echo ""

echo "query string: $QUERY_STRING"
echo "http accept: $HTTP_ACCEPT"

有关环境变量的完整列表,请参见环境部分。

请求主体

请求主体将通过 stdin 传递。以下是一个读取所有请求主体并回显的示例:

#!/bin/sh
echo ""

body=$(cat)

echo "request body: $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)方式输出主体。

Hop-by-hop HTTP

在 CGI 脚本输出中不允许出现 Hop-by-hop HTTP 头。如果在响应中出现,将向客户端返回 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 配置文件的示例:

## 允许 www-data 使用 root 账户运行 /var/www/bin/my-danger-script
www-data ALL=(root) NOPASSWD: /var/www/bin/my-danger-script

## 允许所有 CGI 脚本通过 nginx-cgi 直接以 sudo 启动
www-data ALL=(root) NOPASSWD: SETENV: /var/www/html/cgi-bin/*

我如何使用 chroot 运行 CGI 脚本

强烈不建议使用 chroot 运行 CGI 脚本。因为 chroot 并不是为了安全目的而设计的。它仍然与主机系统共享大量内核空间。例如,在 chroot 进程中运行 ps -ef,主机系统中的所有进程都会返回。这难道不太可怕吗?不,这真的很糟糕,因为您也可以在 chroot 脚本中执行 kill,原因是相同的。而且人们通常在 chroot 环境中以 root 权限运行程序。这是非常糟糕的。它使系统面临的风险比仅以 www-data 运行脚本更高。

如果您想要一个沙箱环境,lxcdockerjails 更适合这个目的。

如果您仍然想使用 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 "files under /:"
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
## 请小心,我在这里下载的是 x86_64 的 busybox 版本,您可能需要更改它
## 您需要 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 以 root 权限运行 chroot

cat >/etc/sudoers.d/www-run-with-chroot <<EOF
## 允许且仅允许 www-data 使用 /var/www/chroot 运行 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

我如何使用 docker 运行 CGI 脚本

在这个示例中,我假设您使用 /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 "files under /:"
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;
}

我如何使用 jails 运行 CGI 脚本

好吧,您是 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 "files under /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

## 尝试一下
/var/www/html/cgi-bin/ls.sh

步骤 1:创建一个 jail

让我们将 jail 放到 /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;

    # 如果您想在 jail 中允许网络访问,请取消注释以下行
    # ip4 = inherit;
    # ip6 = inherit;
    # exec.prestart += "mount_nullfs /etc/resolv.conf /var/www/jail/etc/resolv.conf || true";

    # 如果您还希望在 jail 中使用 `ping`,请取消注释以下行
    # allow.raw_sockets = 1;

    persist; # 如果没有进程运行,则保持 jail
}

并确保以下行出现在 /etc/rc.conf 中:

jail_enable="YES"

启动 jail:

service jail start www-jail

## 尝试一下
jexec www-jail ls /
jexec www-jail /var/www/html/cgi-bin/ls.sh

步骤 2:允许 www 以 root 权限运行 jexec

我在这里使用 sudo。我对 doas 不太熟悉,如果您更喜欢 doas,可以自己尝试。无论如何,FreeBSD 中都没有预装 sudodoas。您需要手动安装其中之一。

cat >/usr/local/etc/sudoers.d/www-jexec <<EOF
## 允许且仅允许 `www` 使用 `www-jail` 运行 `jexec`
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(理想情况下,避免继承 stdinstderr)。以下是一个用 shell 编写的示例。

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

或者如果您熟悉管道操作,只需关闭 stdout(同样,最好也关闭 stdinstderr),HTTP 请求将立即完成。您可以将该进程用作后台进程。

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

## 现在 HTTP 响应已完成,可以做任何您喜欢的事情
sleep 9999

我的 HTTP 请求挂起

正如您所看到的。在 CGI 世界中,HTTP 请求的生命周期取决于管道(stdout)的生命周期。

每个子进程可能会继承 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 "Welcome to the void"

我如何响应重定向

#!/bin/sh

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

我如何获取 HTTP 请求主体

您可以从 stdin 读取请求主体。如果您使用 shell,cat 可以快速将请求主体保存到文件中。

我如何将文件发送给客户端

对于小文件,您可以直接将文件写入 stdout

对于大文件,发送 302 响应要好得多。因为 CGI 响应是流式的,协议无法轻松处理缓存、分块下载或恢复支持。

我想用 Python、Ruby、Perl、C、C++... 编写 CGI

去吧。nginx-cgi 不关心您使用什么语言。只需从环境变量中获取信息,从 stdin 读取请求主体,并将输出写入 stdout

手动

选项

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

在给定的位置块上启用或禁用 cgi 模块。

如果您在这里指定 on,插件将以传统模式工作。它首先解析请求 URI,然后在文档根目录中定位脚本。最后,它将请求 URI 拆分为 SCRIPT_NAMEPATH_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

此选项在许多场景中非常有用,例如:

  • 运行缺少 x-perm 的 CGI 脚本
  • 在执行 CGI 脚本之前执行 sudo
  • 将通用二进制文件包装为 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>

更改 CGI 脚本的 PATH 环境变量。

默认值:/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_TRANSLATEDREMOTE_IDENT)。但不推荐这样做,因为这可能会给您的系统引入混淆问题。

cgi_stderr <path>

将 CGI 的 stderr 重定向到给定文件。

默认情况下,nginx-cgi 会抓取 CGI 脚本的 stderr 输出并将其转储到 nginx 日志中。但此操作有些昂贵,因为它需要创建一个额外的连接来监听 stderr 输出。如果您想避免这种情况,可以使用此选项将 CGI 脚本的 stderr 输出重定向到文件。或者您甚至可以通过重定向到 /dev/null 丢弃所有 stderr 输出。您还可以通过将其设置为 /dev/stderr 将所有 stderr 输出重定向到 nginx 的 stderr。

cgi_rdns <on|off|double> [required]

启用或禁用反向 DNS。

off:禁用 rdns 功能。

on:在启动 CGI 脚本之前进行反向 DNS,并通过 REMOTE_HOST 环境变量将 rdns 结果传递给 CGI 脚本。

double:在反向 DNS 之后,再次进行正向 DNS 检查 rdns 结果。如果结果匹配,则将结果作为 REMOTE_HOST 传递。

required:如果 rdns 失败,则向客户端返回 403、503 或 500。具体取决于 rdns 的失败原因。

如果您开启此选项,还需要在 nginx 中设置 resolver。否则,您将收到 no resolver defined to resolve 的错误。

作者注释:请不要启用此选项,它会使每个请求变慢。此功能可以通过脚本中的 dig -xnslookup 容易地实现。我实现此功能的唯一原因是为了使模块完全符合 rfc3875 标准。

cgi_timeout <t1> [t2]

如果 CGI 进程运行时间过长,则发送 TERM/KILL 信号给该进程。

如果 t1t2 都等于 0,则禁用超时功能。

如果 t1t2 不等于 0,则在超时后将发送 TERMKILL 信号给该进程。

如果 t1t2 都不为零。首先在 t1 时间戳处发送 TERM。如果进程在该时间戳仍然存活,则在 t1+t2 时间戳处再次发送 KILL

如果 t2 不存在,则视为 0

默认值:0 0

标准环境变量

Nginx-cgi 实现了几乎所有 rfc3875 标准变量。如果它们无法覆盖您的所有使用情况,您可以通过 cgi_set_var 添加自己的变量。如果您确实想要,也可以通过 cgi_set_var 覆盖这些变量。

  • AUTH_TYPEREMOTE_USER(rfc3875 标准)

如果 CGI 脚本在授权模块(例如 ngx_http_auth_basic_module)之后,并且授权成功,则该值设置为授权类型(例如 Basic)和授权用户。

如果未启用授权模块,无论客户端是否传递授权头,这两个字段都不存在。

出于安全原因,Authorization 头在 CGI 脚本中不可见。如果您确实想在 CGI 脚本中进行授权,请尝试 cgi_set_var

  • CONTENT_LENGTHCONTENT_TYPE(rfc3875 标准)

与请求头的 Content-LengthContent-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

结合 URL rewritecgi 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=2QUERY_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。只有当正向 DNS 结果与原始地址匹配时,REMOTE_HOST 才会被设置。

有关更多信息,请参见 cgi_rdns

  • REMOTE_IDENT(rfc3875 标准)

出于安全原因,nginx-cgi 插件不支持此功能。

  • REQUEST_METHOD(rfc3875 标准)

请求的方法,例如:GETPOST...

  • 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 标准)

服务器监听的端口,例如 80443...

  • SERVER_PROTOCOL(rfc3875 标准)

客户端和服务器之间使用的协议。例如 HTTP/1.0HTTP/1.1...

  • SERVER_SOFTWARE(rfc3875 标准)

包含 nginx 和版本的字符串,例如 nginx/1.27.4

  • X_(rfc3875 标准)

所有以 X- 开头的 HTTP 请求头将转换为 X_ 变量。例如:

如果头中出现 X-a: 123,则 X_A 将被设置为 123

  • HTTP_(rfc3875 标准)

所有其他 HTTP 请求头将转换为 HTTP_ 变量,例如:

如果头中出现 Accept: */*,则 HTTP_ACCEPT 将被设置为 */*

  • DOCUMENT_ROOT(非标准,由 apache2 实现)

当前位置块的文档根目录,请参见 nginx 中的 root 语句。

  • REMOTE_PORT(非标准,由 apache2 实现)

客户端端口号。

  • REQUEST_SCHEME(非标准,由 apache2 实现)

httphttps

  • 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 上实现这一点非常困难,因为它需要重新触发 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

您可以在 nginx-module-cgi 的 GitHub 仓库中找到此模块的其他配置提示和文档。