Environment-independent 301/302 重定向 PHP
Environment-independent 301/302 redirection with PHP
对于 FastCGI,我 运行 accidental usage Status
header。是否有 pros/cons 在 environment-independent 脚本中使用它?
header('Location: ' . $url, true, 301);
我在 Apache 2.2 上单独使用没有问题(根据 phpinfo()
,服务器使用 FastCGI)。
该脚本针对 Apache 和 nginx(mod_php 和 FastCGI)。 fail-proof 解决方案是什么样的?
HTTP 状态代码作为 HTTP 响应第一行的一部分发出。根据 Fast CGI FAQ 状态 header 是一个特殊的 header 被控制这条线的服务器识别并且它不会被发送到客户端。但是,如果它与非 FastCGI 服务器适配器一起使用,该值将被服务器忽略,并且可以发送 header。
您已有的解决方案是最 environment-independent 可行的方法。唯一的添加是在重定向后立即添加一个 exit
语句以确保脚本终止。
让我们更仔细地看看幕后发生了什么。
以下PHP重定向代码
header('Location: ' . $url, true, 301);
exit;
会在ext/standard/head.c
中调用C代码
PHP_FUNCTION(header)
{
[ code that just parses the arguments omitted ]
sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr);
}
这将依次调用 main/SAPI.c
中的 sapi_header_op
函数
[ ... ]
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;
如果使用FastCGI back-end,添加的header最终会被sapi/cgi/cgi_main.c
中的sapi_cgi_send_headers
函数发送出去
[ ... ]
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);
}
[ ... ]
}
[ ... ]
注意 sapi/apache2handler/sapi_apache2.c
中的 php_apache_sapi_send_headers
函数对 Status
header 没有任何特殊处理,因为它不用于模块通信。
所以通过执行上面的PHP代码
- HTTP 状态行中的响应代码被强制为 301
- 添加位置 header 或替换现有位置
- 脚本退出,因此后续代码无法更改状态或 headers
所有操作都在 SAPI 层中执行,该层是 HTTP 服务器适配器(FastCGI、Apache 模块等)之上的抽象层。这是 cross-environment 和可靠的。
从历史上看,FastCGI 中存在阻止 301 响应正常工作的错误,但这些错误出现在 Web 服务器实现中,PHP 代码无法解决此问题。
另请参阅:
- https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Response_message
- https://github.com/php/php-src/blob/a8d38674b1d75de54bb1d3d1436da759ef8e64d7/ext/standard/head.c
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/main/SAPI.c#L841
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/sapi/cgi/cgi_main.c#L405
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/sapi/apache2handler/sapi_apache2.c#L151
- php 301 redirects actually doing a 302 redirect
对于 FastCGI,我 运行 accidental usage Status
header。是否有 pros/cons 在 environment-independent 脚本中使用它?
header('Location: ' . $url, true, 301);
我在 Apache 2.2 上单独使用没有问题(根据 phpinfo()
,服务器使用 FastCGI)。
该脚本针对 Apache 和 nginx(mod_php 和 FastCGI)。 fail-proof 解决方案是什么样的?
HTTP 状态代码作为 HTTP 响应第一行的一部分发出。根据 Fast CGI FAQ 状态 header 是一个特殊的 header 被控制这条线的服务器识别并且它不会被发送到客户端。但是,如果它与非 FastCGI 服务器适配器一起使用,该值将被服务器忽略,并且可以发送 header。
您已有的解决方案是最 environment-independent 可行的方法。唯一的添加是在重定向后立即添加一个 exit
语句以确保脚本终止。
让我们更仔细地看看幕后发生了什么。
以下PHP重定向代码
header('Location: ' . $url, true, 301);
exit;
会在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);
}
这将依次调用 main/SAPI.c
sapi_header_op
函数
[ ... ]
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;
如果使用FastCGI back-end,添加的header最终会被sapi/cgi/cgi_main.c
sapi_cgi_send_headers
函数发送出去
[ ... ]
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);
}
[ ... ]
}
[ ... ]
注意 sapi/apache2handler/sapi_apache2.c
中的 php_apache_sapi_send_headers
函数对 Status
header 没有任何特殊处理,因为它不用于模块通信。
所以通过执行上面的PHP代码
- HTTP 状态行中的响应代码被强制为 301
- 添加位置 header 或替换现有位置
- 脚本退出,因此后续代码无法更改状态或 headers
所有操作都在 SAPI 层中执行,该层是 HTTP 服务器适配器(FastCGI、Apache 模块等)之上的抽象层。这是 cross-environment 和可靠的。
从历史上看,FastCGI 中存在阻止 301 响应正常工作的错误,但这些错误出现在 Web 服务器实现中,PHP 代码无法解决此问题。
另请参阅:
- https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Response_message
- https://github.com/php/php-src/blob/a8d38674b1d75de54bb1d3d1436da759ef8e64d7/ext/standard/head.c
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/main/SAPI.c#L841
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/sapi/cgi/cgi_main.c#L405
- https://github.com/php/php-src/blob/ebb6f5eae6b37b0202eff325b05932f0b1c28944/sapi/apache2handler/sapi_apache2.c#L151
- php 301 redirects actually doing a 302 redirect