在Nginx中理解和实现FastCGI代理

来自菜鸟教程
跳转至:导航、​搜索

介绍

Nginx 已成为可用的最灵活和最强大的 Web 服务器解决方案之一。 但是,就设计而言,它首先是代理服务器。 这种关注意味着 Nginx 在处理与其他服务器的请求时非常高效。

Nginx 可以使用 http、FastCGI、uwsgi、SCGI 或 memcached 代理请求。 在本指南中,我们将讨论 FastCGI 代理,它是最常见的代理协议之一。

为什么使用 FastCGI 代理?

Nginx 中的 FastCGI 代理通常用于为不或不应直接处理客户端请求的应用程序服务器转换客户端请求。 FastCGI 是一种基于早期 CGI 或通用网关接口的协议,该协议旨在通过不将每个请求作为单独的进程运行来提高性能。 它用于有效地与处理动态内容请求的服务器交互。

Nginx 中 FastCGI 代理的主要用例之一是 PHP 处理。 与 Apache 可以使用 mod_php 模块直接处理 PHP 处理不同,Nginx 必须依赖单独的 PHP 处理器来处理 PHP 请求。 大多数情况下,此处理由 php-fpm 处理,这是一种经过广泛测试可与 Nginx 一起使用的 PHP 处理器。

带有 FastCGI 的 Nginx 可以与使用其他语言的应用程序一起使用,只要配置了一个可访问的组件来响应 FastCGI 请求。

FastCGI 代理基础

通常,代理请求涉及代理服务器,在这种情况下是 Nginx,将请求从客户端转发到后端服务器。 Nginx 用于定义要代理以使用 FastCGI 协议的实际服务器的指令是 fastcgi_pass

例如,要将任何匹配的 PHP 请求转发到专门使用 FastCGI 协议处理 PHP 处理的后端,基本位置块可能如下所示:

# server context

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

上面的代码片段实际上并不能开箱即用,因为它提供的信息太少。 每当建立代理连接时,必须转换原始请求以确保代理请求对后端服务器有意义。 由于我们正在使用 FastCGI 通道更改协议,这涉及到一些额外的工作。

虽然 http-to-http 代理主要涉及增加 http 标头以确保后端具有代表客户端响应代理服务器所需的信息,但 FastCGI 是一个无法读取 http 标头的单独协议。 由于这种考虑,任何相关信息都必须通过其他方式传递到后端。

使用 FastCGI 协议时传递额外信息的主要方法是使用参数。 后台服务器应该被配置为读取和处理这些,根据它找到的内容修改它的行为。 Nginx 可以使用 fastcgi_param 指令设置 FastCGI 参数。

在 PHP 的 FastCGI 代理场景中实际工作的最低配置是这样的:

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

在上述配置中,我们设置了两个 FastCGI 参数,分别称为 REQUEST_METHODSCRIPT_FILENAME。 为了让后端服务器了解请求的性质,这些都是必需的。 前者告诉它应该执行什么类型的操作,而后者告诉上游要执行哪个文件。

在示例中,我们使用了一些 Nginx 变量来设置这些参数的值。 $request_method 变量将始终包含客户端请求的 http 方法。

SCRIPT_FILENAME 参数设置为 $document_root 变量和 $fastcgi_script_name 变量的组合。 $document_root 将包含基目录的路径,由 root 指令设置。 $fastcgi_script_name 变量将设置为请求 URI。 如果请求 URI 以斜杠 (/) 结尾,则 fastcgi_index 指令的值将附加到末尾。 这种类型的自引用位置定义是可能的,因为我们在与 Nginx 实例相同的机器上运行 FastCGI 处理器。

让我们看另一个例子:

# server context
root /var/www/html;

location /scripts {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

. . .

如果选择上述位置来处理对 /scripts/test/ 的请求,则 SCRIPT_FILENAME 的值将是 root 指令的值、请求 URI 和fastcgi_index 指令。 在本例中,参数将设置为 /var/www/html/scripts/test/index.php

我们对上述配置进行了另一项重大更改,即我们使用 Unix 套接字而不是网络套接字指定了 FastCGI 后端。 Nginx 可以使用任一类型的接口连接到上游的 FastCGI。 如果 FastCGI 处理器位于同一主机上,通常建议使用 Unix 套接字以确保安全。

突破 FastCGI 配置

可维护代码的一个关键规则是尝试遵循 DRY(“不要重复自己”)原则。 这有助于减少错误,提高可重用性,并允许更好的组织。 考虑到管理 Nginx 的核心建议之一是始终在其最广泛的适用范围内设置指令,这些基本目标也适用于 Nginx 配置。

在处理 FastCGI 代理配置时,大多数使用实例将共享大部分配置。 由于这一点以及 Nginx 继承模型的工作方式,在一般范围内声明参数几乎总是有利的。

在父上下文中声明 FastCGI 配置详细信息

减少重复的一种方法是在更高的父上下文中声明配置详细信息。 实际 fastcgi_pass 之外的所有参数都可以在更高级别指定。 它们将向下级联到传球发生的位置。 这意味着多个位置可以使用相同的配置。

例如,我们可以修改上一节中的最后一个配置片段,使其在多个位置有用:

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

在上面的示例中,fastcgi_param 声明和 fastcgi_index 指令在后面的两个位置块中都可用。 这是删除重复声明的一种方法。

然而,上述配置有一个严重的缺点。 如果在下层上下文中声明了任何 fastcgi_param,则将继承父上下文中的 fastcgi_param 值中的 none。 您要么使用 only 继承的值,要么不使用它们。

fastcgi_param 指令是 Nginx 用语中的 array 指令。 从用户的角度来看,数组指令基本上是可以在单个上下文中多次使用的任何指令。 每个后续声明都会将新信息附加到 Nginx 从先前声明中知道的内容。 fastcgi_param 指令被设计为数组指令,以允许用户设置多个参数。

数组指令以与其他一些指令不同的方式继承到子上下文。 仅当数组指令的信息不存在于子上下文 中的任何位置时,它们才会继承到子上下文。 这意味着如果您在您的位置使用 fastcgi_param,它将有效地完全清除从父上下文继承的值。

例如,我们可以稍微修改上面的配置:

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

乍一看,您可能会认为 REQUEST_METHODSCRIPT_FILENAME 参数将被继承到第二个位置块中,而 QUERY_STRING 参数也可用于该特定上下文。

实际发生的是父 fastcgi_param 值的 all 在第二个上下文中被清除,并且仅设置了 QUERY_STRING 参数。 REQUEST_METHODSCRIPT_FILENAME 参数将保持未设置状态。

关于同一上下文中参数的多个值的说明

在这一点上绝对值得一提的是在单个上下文中为相同参数设置多个值的含义。 我们以下面的例子作为讨论点:

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $request_uri;

    fastcgi_param DOCUMENT_ROOT initial;
    fastcgi_param DOCUMENT_ROOT override;

    fastcgi_param TEST one;
    fastcgi_param TEST two;
    fastcgi_param TEST three;

    fastcgi_pass 127.0.0.1:9000;
}

. . .

在上面的示例中,我们在单个上下文中多次设置了 TESTDOCUMENT_ROOT 参数。 由于 fastcgi_param 是一个数组指令,因此每个后续声明都会添加到 Nginx 的内部记录中。 TEST 参数将在数组中声明,将其设置为 onetwothree

此时需要意识到的重要一点是,所有这些都将被传递到 FastCGI 后端,而无需 Nginx 的任何进一步处理。 这意味着完全由所选的 FastCGI 处理器决定如何处理这些值。 不幸的是,不同的 FastCGI 处理器处理传递的值 完全不同

例如,如果 PHP-FPM 接收到上述参数,则 final 值将被解释为覆盖任何先前的值。 因此在这种情况下,TEST 参数将设置为 three。 同样,DOCUMENT_ROOT 参数将设置为 override

但是,如果将上述值传递给 FsgiWrap 之类的东西,则这些值的解释会非常不同。 首先,它进行初始传递以决定使用哪些值来运行脚本。 它将使用 initialDOCUMENT_ROOT 值来查找脚本。 但是,当它将实际参数传递给脚本时,它将传递最终值,就像 PHP-FPM 一样。

这种不一致和不可预测性意味着您不能也不应该依赖后端在多次设置相同参数时正确解释您的意图。 唯一安全的解决方案是每个参数只声明一次。 这也意味着不存在使用 fastcgi_param 指令安全地覆盖默认值这样的事情。

使用 Include 从单独的文件中获取 FastCGI 配置

还有另一种方法可以分离出常见的配置项。 我们可以使用 include 指令将单独文件的内容读入指令声明的位置。

这意味着我们可以将所有常见的配置项保存在一个文件中,并将其包含在我们需要的配置中的任何位置。 由于 Nginx 会将实际文件内容放置在调用 include 的位置,因此我们不会从父上下文向下继承到子上下文。 这将防止 fastcgi_param 值被清除,允许我们根据需要设置其他参数。

首先,我们可以在配置目录的单独文件中设置我们常用的 FastCGI 配置值。 我们将此文件称为 fastcgi_common

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

现在,我们可以在希望使用这些配置值的任何地方读取此文件:

# server context
root /var/www/html;

location /scripts {
    include fastcgi_common;

    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    include fastcgi_common;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;

    fastcgi_index index.php;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

在这里,我们将一些常见的 fastcgi_param 值移动到默认 Nginx 配置目录中名为 fastcgi_common 的文件中。 然后,当我们想要插入其中声明的值时,我们会获取该文件。

关于此配置有几点需要注意。

首先,我们没有在我们计划获取的文件中放置我们可能希望基于每个位置自定义的任何值。 由于我们上面提到的在为同一个参数设置多个值时会出现解释问题,并且因为非数组指令只能在每个上下文中设置一次,所以只将项目放置在您不想更改的公共文件中。 我们可能希望在每个上下文的基础上自定义的每个指令(或参数键)都应该被排除在公共文件之外。

您可能注意到的另一件事是我们在第二个位置块中设置了一些额外的 FastCGI 参数。 这是我们希望达到的能力。 我们能够根据需要设置额外的 fastcgi_param 参数,而不会消除常用值。

使用 fastcgi_params 文件或 fastcgi.conf 文件

考虑到上述策略,Nginx 开发人员和许多分发打包团队致力于提供一组合理的通用参数,您可以将这些参数包含在您的 FastCGI 传递位置中。 这些称为 fastcgi_paramsfastcgi.conf

这两个文件在很大程度上是相同的,唯一的区别实际上是我们之前讨论的关于为单个参数传递多个值的问题的结果。 fastcgi_params 文件不包含 SCRIPT_FILENAME 参数的声明,而 fastcgi.conf 文件包含。

fastcgi_params 文件的可用时间要长得多。 为了避免破坏依赖 fastcgi_params 的配置,当决定为 SCRIPT_FILENAME 提供默认值时,需要创建一个新文件。 不这样做可能会导致在公共文件和 FastCGI 传递位置中设置该参数。 这在 Martin Fjordvald 关于这两个文件的历史 的优秀文章中有详细描述。

许多流行发行版的包维护者选择只包含这些文件之一或精确地镜像它们的内容。 如果您只有其中一个可用,请使用您拥有的那个。 随意修改它以满足您的需求。

如果您拥有这两个文件,对于大多数 FastCGI 传递位置,最好包含 fastcgi.conf 文件,因为它包含 SCRIPT_FILENAME 参数的声明。 这通常是可取的,但在某些情况下您可能希望自定义此值。

这些可以通过引用它们相对于根 Nginx 配置目录的位置来包含。 当 Nginx 使用包管理器安装时,根 Nginx 配置目录通常类似于 /etc/nginx

您可以包含这样的文件:

# server context

location ~ \.php$ {
    include fastcgi_params;
    # You would use "fastcgi_param SCRIPT_FILENAME . . ." here afterwards
    
    . . .

}

或者像这样:

# server context

location ~ \.php$ {
    include fastcgi.conf;

    . . .

}

重要的 FastCGI 指令、参数和变量

在上面的部分中,我们设置了相当多的参数,通常是 Nginx 变量,作为演示其他概念的一种方式。 我们还介绍了一些 FastCGI 指令,没有过多解释。 在本节中,我们将讨论一些要设置的常用指令、您可能需要修改的参数以及可能包含您需要的信息的一些变量。

常见的 FastCGI 指令

以下代表了使用 FastCGI 通道的一些最有用的指令:

  • fastcgi_pass:将当前上下文中的请求传递到后端的实际指令。 这定义了可以到达 FastCGI 处理器的位置。
  • fastcgi_param:可用于将参数设置为值的数组指令。 大多数情况下,它与 Nginx 变量结合使用,将 FastCGI 参数设置为特定于请求的值。
  • try_files:不是特定于 FastCGI 的指令,而是在 FastCGI 传递位置中使用的通用指令。 这通常用作请求清理例程的一部分,以确保请求的文件在传递给 FastCGI 处理器之前存在。
  • include:同样,它不是特定于 FastCGI 的指令,而是在 FastCGI 传递上下文中得到大量使用的指令。 大多数情况下,这用于在多个位置包含常见的共享配置详细信息。
  • fastcgi_split_path_info:该指令定义了一个带有两个捕获组的正则表达式。 第一个捕获的组用作 $fastcgi_script_name 变量的值。 第二个捕获的组用作 $fastcgi_path_info 变量的值。 这两者通常用于正确解析请求,以便处理器知道请求的哪些部分是要运行的文件,哪些部分是要传递给脚本的附加信息。
  • fastcgi_index:这定义了应该附加到以斜杠(/)结尾的$fastcgi_script_name值的索引文件。 如果 SCRIPT_FILENAME 参数设置为 $document_root$fastcgi_script_name 并且位置块配置为接受文件后带有信息的请求,这通常很有用。
  • fastcgi_intercept_errors:该指令定义从 FastCGI 服务器接收到的错误是否应该由 Nginx 处理或直接传递给客户端。

上述指令代表了您在设计典型 FastCGI 通道时将使用的大部分内容。 您可能不会一直使用所有这些,但我们可以开始看到它们与我们将在接下来讨论的 FastCGI 参数和变量非常密切地交互。

FastCGI 使用的通用变量

在我们讨论您可能在 FastCGI 通道中使用的参数之前,我们应该先谈谈一些常见的 Nginx 变量,我们将在设置这些参数时利用这些变量。 其中一些是由 Nginx 的 FastCGI 模块定义的,但大多数来自 Core 模块。

  • $query_string 或 $args:原始客户端请求中给出的参数。
  • $is_args:将等于“?” 如果请求中有参数,否则将设置为空字符串。 这在构造可能有或没有参数的参数时很有用。
  • $request_method:表示原始客户端请求方法。 这对于确定是否应在当前上下文中允许操作很有用。
  • $content_type:设置为 Content-Type 请求标头。 如果用户的请求是 POST,则代理需要此信息才能正确处理随后的内容。
  • $content_length:设置为来自客户端的 Content-Length 标头的值。 任何客户端 POST 请求都需要此信息。
  • $fastcgi_script_name:这将包含要运行的脚本文件。 如果请求以斜杠 (/) 结尾,则 fastcgi_index 指令的值将附加到末尾。 如果使用 fastcgi_split_path_info 指令,此变量将设置为该指令定义的第一个捕获组。 此变量的值应指示要运行的实际脚本。
  • $request_filename:此变量将包含请求文件的文件路径。 它通过获取当前文档根的值来获取此值,同时考虑 rootalias 指令以及 $fastcgi_script_name 的值。 这是分配 SCRIPT_FILENAME 参数的一种非常灵活的方式。
  • $request_uri:从客户端收到的整个请求。 这包括脚本、任何附加路径信息以及任何查询字符串。
  • $fastcgi_path_info:此变量包含可能在请求中的脚本名称之后可用的附加路径信息。 此值有时包含要执行的脚本应该知道的另一个位置。 当使用 fastcgi_split_path_info 指令时,此变量从第二个捕获的正则表达式组中获取其值。
  • $document_root:此变量包含当前文档根值。 这将根据 rootalias 指令进行设置。
  • $uri:此变量包含应用了规范化的当前 URI。 由于某些重写或内部重定向的指令会对 URI 产生影响,因此此变量将表达这些更改。

如您所见,在决定如何设置 FastCGI 参数时,您可以使用很多变量。 其中许多是相似的,但有一些细微的差异会影响脚本的执行。

常用 FastCGI 参数

FastCGI 参数表示我们希望向我们发送请求的 FastCGI 处理器提供的键值信息。 并非每个应用程序都需要相同的参数,因此您经常需要查阅应用程序的文档。

其中一些参数是处理器正确识别要运行的脚本所必需的。 其他可用于脚本,如果配置为依赖于设置的参数,可能会修改其行为。

  • QUERY_STRING:此参数应设置为客户端提供的任何查询字符串。 这通常是在“?”之后提供的键值对。 在 URI 中。 通常,此参数设置为 $query_string$args 变量,这两个变量应包含相同的数据。
  • REQUEST_METHOD:此参数向 FastCGI 处理器指示客户端请求的操作类型。 这是为使传递正确运行而需要设置的少数参数之一。
  • CONTENT_TYPE:如果上面设置的请求方式为“POST”,则必须设置该参数。 它指示 FastCGI 处理器应该期望的内容类型。 这几乎总是设置为 $content_type 变量,该变量是根据原始请求中的信息设置的。
  • CONTENT_LENGTH:如果请求方式为“POST”,则必须设置该参数。 这表示内容长度。 这几乎总是设置为 $content_length,这是一个从原始客户端请求中的信息获取其值的变量。
  • SCRIPT_NAME:此参数用于指示将要运行的主脚本的名称。 这是一个极其重要的参数,可以根据您的需要以多种方式设置。 通常,这设置为 $fastcgi_script_name,它应该是请求 URI,如果以斜杠结尾,则应为请求 URI 附加 fastcgi_index,如果使用 [,则应为第一个捕获的组X176X]。
  • SCRIPT_FILENAME:此参数指定要运行的脚本在磁盘上的实际位置。 由于它与 SCRIPT_NAME 参数的关系,一些指南建议您使用 $document_root$fastcgi_script_name。 另一种具有许多优点的替代方法是使用 $request_filename
  • REQUEST_URI:这应该包含完整的、未修改的请求 URI、完整的要运行的脚本、附加路径信息和任何参数。 一些应用程序更喜欢自己解析这些信息。 此参数为他们提供了执行此操作所需的信息。
  • PATH_INFO:如果 cgi.fix_pathinfo 在 PHP 配置文件中设置为“1”,这将包含脚本名称后添加的任何附加路径信息。 这通常用于定义脚本应该执行的文件参数。 如果脚本请求没有通过其他方式进行清理,将 cgi.fix_pathinfo 设置为“1”可能会产生安全隐患(我们将在稍后讨论)。 有时这被设置为 $fastcgi_path_info 变量,它包含来自 fastcgi_split_path_info 指令的第二个捕获组。 其他时候,需要使用临时变量,因为该值有时会被其他处理破坏。
  • PATH_TRANSLATED:此参数将PATH_INFO中包含的路径信息映射到实际的文件系统路径。 通常,这将设置为 $document_root$fastcgi_path_info 之类的值,但有时后面的变量必须由临时保存的变量替换,如上所示。

在传递给 FastCGI 之前检查请求

我们尚未涵盖的一个非常重要的主题是如何安全地将动态请求传递到您的应用程序服务器。 将所有请求传递给后端应用程序,无论其有效性如何,不仅效率低下,而且很危险。 攻击者可能会制作恶意请求以试图让您的服务器运行任意代码。

为了解决这个问题,我们应该确保我们只向我们的 FastCGI 处理器发送合法请求。 根据我们特定设置的需要以及 FastCGI 处理器是否与我们的 Nginx 实例位于同一系统上,我们可以通过多种方式执行此操作。

应该告知我们如何设计配置的一个基本规则是,我们绝不应该允许对用户文件进行任何处理和解释。 恶意用户将有效代码嵌入看似无害的文件(例如图像)中相对容易。 一旦这样的文件上传到我们的服务器,我们必须确保它永远不会进入我们的 FastCGI 处理器。

我们在这里试图解决的主要问题是 CGI 规范中实际指定的问题。 该规范允许您指定要运行的脚本文件,然后是脚本可以使用的附加路径信息。 这种执行模型允许用户请求一个看起来像合法脚本的 URI,而实际执行的部分将在路径的前面。

考虑对 /test.jpg/index.php 的请求。 如果您的配置只是将每个以 .php 结尾的请求传递给您的处理器,而没有测试其合法性,则处理器(如果遵循规范)将检查该位置并在可能的情况下执行它。 如果 没有 找到该文件,它将遵循规范并尝试执行 /test.jpg 文件,将 /index.php 标记为脚本的附加路径信息。 正如你所看到的,当结合用户上传的想法时,这可能会导致一些非常不受欢迎的后果。

有许多不同的方法可以解决这个问题。 如果您的应用程序不依赖此额外路径信息进行处理,最简单的方法就是在您的处理器中将其关闭。 对于 PHP-FPM,您可以在 php.ini 文件中将其关闭。 例如,在 Ubuntu 系统上,您可以编辑此文件:

sudo nano /etc/php5/fpm/php.ini

只需搜索 cgi.fix_pathinfo 选项,取消注释并将其设置为“0”以禁用此“功能”:

cgi.fix_pathinfo=0

重新启动您的 PHP-FPM 进程以进行更改:

sudo service php5-fpm restart

这将导致 PHP 只尝试在路径的最后一个组件上执行。 所以在我们上面的例子中,如果 /test.jpg/index.php 文件不存在,PHP 会正确地出错而不是尝试执行 /test.jpg

如果我们的 FastCGI 处理器与 Nginx 实例在同一台机器上,另一种选择是在将文件传递给处理器之前简单地检查磁盘上的文件是否存在。 如果 /test.jgp/index.php 文件不存在,则会出错。 如果是,则将其发送到后端进行处理。 在实践中,这将导致与我们上面的大部分相同的行为:

location ~ \.php$ {
        try_files $uri =404;

        . . .

}

如果您的应用程序 确实 依赖路径信息行为进行正确解释,您仍然可以通过在决定是否将请求发送到后端之前进行检查来安全地允许此行为。

例如,我们可以专门匹配允许不可信上传的目录,并确保它们不会传递给我们的处理器。 例如,如果我们的应用程序的上传目录是 /uploads/,我们可以在计算任何正则表达式之前创建一个这样的位置块:

location ^~ /uploads {
}

在内部,我们可以禁用对 PHP 文件的任何类型的处理:

location ^~ /uploads {
    location ~* \.php$ { return 403; }
}

父位置将匹配任何以 /uploads 开头的请求,任何处理 PHP 文件的请求都将返回 403 错误,而不是将其发送到后端。

您还可以使用 fastcgi_split_path_info 指令手动定义应解释为脚本的请求部分和应使用正则表达式定义为额外路径信息的部分。 这允许您仍然依赖路径信息功能,但要准确定义您认为脚本的内容和路径。

例如,我们可以设置一个位置块,它将以 .php 结尾的路径组件的第一个实例视为要运行的脚本。 其余的将被视为额外的路径信息。 这意味着在请求 /test.jpg/index.php 的情况下,可以将整个路径作为脚本名称发送到处理器,而无需额外的路径信息。

此位置可能如下所示:

location ~ [^/]\.php(/|$) {

    fastcgi_split_path_info ^(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

上面的块应该适用于 cgi.fix_pathinfo 设置为“1”以允许额外路径信息的 PHP 配置。 在这里,我们的位置块不仅匹配以 .php 结尾的请求,而且还匹配那些在斜杠 (/) 之前表示附加目录组件的 .php 的请求。

在块内部,fastcgi_split_path_info 指令定义了两个使用正则表达式捕获的组。 第一组匹配 URI 从开始到 .php 的第一个实例的部分,并将其放在 $fastcgi_script_name 变量中。 然后,它将从该点开始的任何信息放入第二个捕获的组中,并将其存储在名为 $fastcgi_path_info 的变量中。

我们使用 set 指令将此时保存在 $fastcgi_path_info 中的值存储到名为 $orig_path 的变量中。 这是因为 $fastcgi_path_info 变量将被我们的 try_files 指令立即清除。

我们使用 try_files 测试上面捕获的脚本名称。 这是一个文件操作,将确保我们尝试运行的脚本在磁盘上。 但是,这也有清除 $fastcgi_path_info 变量的副作用。

在执行了常规的 FastCGI 通道之后,我们照常设置 SCRIPT_FILENAME。 我们还将 PATH_INFO 设置为我们卸载到 $orig_path 变量中的值。 虽然我们的 $fastcgi_path_info 被清除了,但它的原始值仍然保留在这个变量中。 我们还设置了 PATH_TRANSLATED 参数以将额外的路径信息映射到它在磁盘上存在的位置。 我们通过将 $document_root 变量与 $orig_path 变量相结合来做到这一点。

这允许我们构造像 /index.php/users/view 这样的请求,以便我们的 /index.php 文件可以处理有关 /users/view 目录的信息,同时避免运行 /test.jpg/index.php 的情况。 它将始终将脚本设置为以 .php 结尾的最短组件,从而避免此问题。

如果我们需要更改脚本文件的位置,我们甚至可以使用别名指令来完成这项工作。 我们只需要在我们的位置标头和 fastcgi_split_path_info 定义中都考虑到这一点:

location ~ /test/.+[^/]\.php(/|$) {

    alias /var/www/html;

    fastcgi_split_path_info ^/test(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

这些将允许您安全地运行使用 PATH_INFO 参数的应用程序。 请记住,您必须将 php.ini 文件中的 cgi.fix_pathinfo 选项更改为“1”才能使其正常工作。 您可能还需要关闭 php-fpm.conf 文件中的 security.limit_extensions

结论

希望现在您对 Nginx 的 FastCGI 代理功能有了更好的了解。 这种能力使 Nginx 可以在快速连接处理和提供静态内容方面发挥其优势,同时将动态内容的责任转移到更适合的软件上。 FastCGI 允许 Nginx 以高性能和安全的配置与大量应用程序一起工作。