使用PHP进行与环境无关的301/302重定向

Est*_*ask 10 php apache fastcgi nginx http-headers

偶然使用Status了FastCGI 的标题.在与环境无关的脚本中使用它有利有弊吗?

header('Location: ' . $url, true, 301);
Run Code Online (Sandbox Code Playgroud)

单独在Apache 2.2上没有问题(根据phpinfo(),服务器使用FastCGI).

该脚本针对Apache和nginx(mod_php和FastCGI).防故障解决方案会是什么样的?

ant*_*tix 4

HTTP 状态代码作为 HTTP 响应第一行的一部分发出。根据Fast CGI FAQ, Status 标头是由控制该线路的服务器识别的特殊标头,并且不会发送到客户端。但是,如果它与非 FastCGI 服务器适配器一起使用,则服务器会忽略该值,并且可以发送标头。

您已有的解决方案是最独立于环境的方式。唯一的添加是exit在重定向后立即添加一条语句,以确保脚本终止。

让我们更仔细地看看幕后发生了什么。

以下PHP重定向代码

header('Location: ' . $url, true, 301);
exit;
Run Code Online (Sandbox Code Playgroud)

将调用C代码ext/standard/head.c

PHP_FUNCTION(header)
{
    [ code that just parses the arguments omitted ]

    sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr);
}
Run Code Online (Sandbox Code Playgroud)

这将依次调用该sapi_header_op函数main/SAPI.c

[ ... ]

switch (op) {

    [ ... ]

    case SAPI_HEADER_ADD:
    case SAPI_HEADER_REPLACE:
    case SAPI_HEADER_DELETE: {
            sapi_header_line *p = arg;

            if (!p->line || !p->line_len) {
                return FAILURE;
            }
            header_line = p->line;
            header_line_len = p->line_len;
            http_response_code = p->response_code;
            break;
        }

[ code that splits header line by colon, trims whitespace etc ]

[ special headers handling code, including setting 302 if Location  ]

if (http_response_code) {
    sapi_update_response_code(http_response_code);
}

sapi_header_add_op(op, &sapi_header);
return SUCCESS;
Run Code Online (Sandbox Code Playgroud)

如果使用FastCGI后端,添加的标头最终将通过sapi_cgi_send_headers中的函数发送出去sapi/cgi/cgi_main.c

[ ... ]
if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
{
    [ emit status line if cgi.rfc2616-headers is set ]

    [ Handle a case where there is a user supplied status line ]

    [ Handle a case where there is already a user supplied status header ]

    [ if none of the above ]

        if (err->str) {
            len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->str);
        } else {
            len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
        }
     [ ... ]
}
[ ... ]
Run Code Online (Sandbox Code Playgroud)

请注意,php_apache_sapi_send_headers函数 insapi/apache2handler/sapi_apache2.c没有对Status标头进行任何特殊处理,因为它不用于模块通信。

所以通过执行上面的PHP代码

  1. HTTP 状态行中的响应代码强制为 301
  2. 添加位置标头或替换现有标头
  3. 脚本退出,因此后续代码无法更改状态或标题

所有操作都在 SAPI 层中执行,该层是 HTTP 服务器适配器(FastCGI、Apache 模块等)之上的抽象层。这是跨环境且可靠的。

从历史上看,FastCGI 中曾存在一些错误,导致 301 响应无法正常工作,但这些错误是在 Web 服务器实现中,并且 PHP 代码无法解决该问题。

也可以看看: