下面的脚本 1 是 bash,位于https://example.com/cgi-bin/test
。它在获取时生成输出“正在建设中”。它回显Status
和Content-type
标题以及一些 HTML。如果我尝试回显整个 HTML 文档,Apache 只会抱怨标题无效。
下面的脚本 2 是 php,位于https://example.com/cgi-bin/test2.php
。与 bash 脚本不同,此脚本返回 HTML 文档。
为什么脚本 2 可以发送整个 HTML 文档,而脚本 1 却不能?
脚本 1
#!/bin/bash
cat <<'EOF'
Status: 200 OK
Content-type: text/html
<p>Under construction.</p>
EOF
脚本 2
<?php
print <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
...etc
</head>
<body>
...etc
</body>
</html>
EOF;
?>
编辑
php
有两种版本:CLI 和 CGI 版本。如果您只是从命令行运行脚本 2,php test2.php
那么仅有的产生的输出php
正是您所看到的:HTML 文档php-cgi
是 CGI 版本(在 Ubuntu/Deb 上安装为apt install php-cgi
)。Apache(有效地)运行 CGI 版本(在现实生活中,它执行此操作略有不同,但结果相同):
$ php-cgi test2.php
Content-type: text/html; charset=UTF-8
<!DOCTYPE html>
...rest of doc
CGI 脚本至少必须返回Content-type
Apache(但可以返回更多标头,包括Status
)。所以答案是两个都脚本能够工作是因为脚本 1 明确返回了Content-type
,而的底层 CGI 版本也php
做了同样的事。
bash 脚本可以返回整个 HTML 文档,只要它还返回Content-type
.
答案1
在 CGI 中,您需要发送 Content-Type。PHP 会为您生成它。(通过浏览器检查输出,即使您在代码中看不到它,您也会看到它。)
我有一个用 C 语言编写的 CGI,你确实需要内容类型;
就我的情况而言;
printf("内容类型:text/html;charset=us-ascii\n\n");
如果您需要更改 php 中的标题,则必须在脚本开始时调用标题。
IE;
标头('内容类型:application/json');
答案2
在这种情况下,PHP 和 CGI 是两种不同的东西。
CGI 是程序(在本例中为 bash 脚本)与 Web 服务器之间的接口。此接口指定 Web 服务器与程序之间的通信。
该标准要求程序返回全部标头(包括状态标头)位于实际内容之前。在 HTTP 中,标头和正文由一行分隔 - 因此格式如下
Header
Header
Content
在标题之后,您可以自由地包含完整的 HTML 文档 - 或者与您发送的标题匹配的任何其他类型的数据。
PHP 为您做出了一些假设,除非您覆盖它,否则它会自动设置内容类型、状态代码等。
答案3
如果通过 Apache 通过 CGI 调用脚本,则需要包含有效的 CGI 标头响应,而不是 HTTP 响应。我认为从本质上讲(详见下文),您需要从文件中删除“状态:200 OK”,这样它可能会起作用。
Apache 自己写了一篇很好的文章(https://httpd.apache.org/docs/2.2/howto/cgi.html) 显示获得有效响应所需的最低限度。
客户端收到的示例标头可能如下:
HTTP/1.x 200 OK
Transfer-Encoding: chunked
Date: Tue, 06 Dec 2021 19:58:00 GMT
Server: My_Bash_Script
Connection: close
X-Powered-By: My_Bash_Script
Pragma: public
Expires: Tue, 06 Dec 2021 20:58:00 GMT
Cache-Control: max-age=3600, public
Last-Modified: Tue, 06 Dec 2021 20:58:00 GMT
Content-Encoding: gzip
Vary: Accept-Encoding, Cookie, User-Agent
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html>
<head><title>Under construction</title>
<body><p>Under construction.</p></body>
</html>
但是你的CGI,只需要从“Content-Type:text/html;charset=UTF-8”向下发送。
这里有一篇相当不错的文章来解释标题: https://code.tutsplus.com/tutorials/http-headers-for-dummies--net-8039
对于通过 Apache 提供的 PHP 文件,存在几层通信:
- Apache 在 TCP 端口上发出请求。如果通过 HTTP,则包括请求标头。
- Apache 运行任何规则(例如 mod_rewrite)并处理所需的任何 SSL 连接/握手。
- 然后 Apache 检测文件扩展名是 PHP,并通过 PHP 解释器调用 PHP 脚本。
- PHP 代码被解释并转换成静态字符串(希望如此 :-),该字符串返回给 Apache 并包含 HTML 代码。
- 然后,Apache 将标头信息连同任何其他出站处理一起添加到 HTML 页面。
- 它被序列化并通过 TCP 连接传回客户端。
探索标头的另一种好方法是使用 Firefox/Chrome 开发人员工具(在 Firefox 中按 F12 即可打开它们)。打开开发人员工具后,转到“网络”选项卡并重新加载页面(Windows/Linux 上按 Ctrl + R)。有一个“原始”选项,您可以在其中查看发送和接收的确切数据。
最后,如果您发现某个网站,或者您的 CGI 是通过 http 而不是 https 提供的,您可以安装 Wireshark (https://www.wireshark.org) 并轻松监控流量对话以了解您发送的被误解的内容与 Apache 提供的正常静态 html 页面对话之间的差异。
PS(2022 年),如果您确实在运行 Apache 2.2:
- 您可能会错过 Apache2.4 中的其他功能,这些功能可能会帮助您更快地到达您想去的地方:https://httpd.apache.org/docs/2.4/new_features_2_4.html
- 请注意,Apache 2.2 已停止使用(自 2017 年起),并且包含许多漏洞:https://httpd.apache.org/security/vulnerabilities_22.html