下面的腳本 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
有 2 種風格: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 中的標頭,則必須在腳本開頭呼叫 header 。
IE;
header('內容類型: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/vulnerability_22.html