檢測遠端 URL 可用性、監控服務存活、爬蟲預檢都需要獲取 HTTP 狀態碼。PHP 提供 get_headers() 簡便方法與 cURL 完整控制兩種方案,本文整合雙重實現,支援僅取狀態碼與完整 Header 解析兩種模式。
<?php
/**
* 獲取遠端 URL HTTP 狀態資訊
*
* @param string $url 目標 URL
* @param bool $fullHeader true=回傳完整 header 陣列, false=僅回傳狀態碼
* @return array|int|false 成功回傳狀態碼或 header 陣列,失敗回傳 false
*/
function getHttpStatus(string $url, bool $fullHeader = false) {
// 方案一:get_headers (簡單、無需擴充)
$headers = @get_headers($url, 1); // 第 2 參數 1 = 關聯陣列格式
if ($headers !== false) {
if ($fullHeader) {
return $headers; // 完整 header 陣列
}
// 解析狀態行:HTTP/1.1 200 OK
$statusLine = $headers[0] ?? '';
if (preg_match('#HTTP/\d\.\d\s+(\d{3})#', $statusLine, $matches)) {
return (int)$matches[1];
}
}
// 方案二:cURL 兜底 (支援更多協定、超時控制、重導向處理)
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_HEADER => true, // 包含 header
CURLOPT_NOBODY => true, // HEAD 請求,不下載 body
CURLOPT_RETURNTRANSFER => true, // 回傳字串
CURLOPT_TIMEOUT => 10, // 連線超時 10 秒
CURLOPT_CONNECTTIMEOUT => 5, // 建立連線超時 5 秒
CURLOPT_FOLLOWLOCATION => true, // 追蹤 3xx 重導向
CURLOPT_MAXREDIRS => 5, // 最大重導向次數
CURLOPT_SSL_VERIFYPEER => false, // 開發環境跳過 SSL 驗證
CURLOPT_USERAGENT => 'PHP-HTTP-Status-Checker/1.0',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
error_log("cURL error for {$url}: {$curlError}");
return false;
}
if ($fullHeader && $response) {
// 解析原始 header 字串為關聯陣列
return parseRawHeaders($response);
}
return $httpCode ?: false;
}
/**
* 解析 cURL 原始 header 字串
*/
function parseRawHeaders(string $rawHeaders): array {
$headers = [];
$lines = explode("\n", $rawHeaders);
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') continue;
if (str_starts_with($line, 'HTTP/')) {
$headers[0] = $line; // 狀態行
} elseif (strpos($line, ':') !== false) {
[$key, $value] = explode(':', $line, 2);
$key = trim($key);
$value = trim($value);
// 處理重複 header (如 Set-Cookie)
if (isset($headers[$key])) {
$headers[$key] = (array)$headers[$key];
$headers[$key][] = $value;
} else {
$headers[$key] = $value;
}
}
}
return $headers;
}
?>
<?php
// 1. 僅獲取狀態碼 (預設)
$code = getHttpStatus('https://www.example.com');
var_dump($code); // int(200)
// 2. 獲取完整 Header
$headers = getHttpStatus('https://www.example.com', true);
/*
array(10) {
[0]=> "HTTP/1.1 200 OK"
["Server"]=> "nginx"
["Date"]=> "Mon, 15 Jan 2024 06:21:35 GMT"
["Content-Type"]=> "text/html; charset=UTF-8"
["Content-Length"]=> "26898"
["Last-Modified"]=> "Mon, 15 Jan 2024 06:16:00 GMT"
["Connection"]=> "keep-alive"
["Vary"]=> "Accept-Encoding"
["ETag"]=> ""5779ff20-6912""
["Accept-Ranges"]=> "bytes"
}
*/
// 3. 批次檢測多個 URL
$urls = [
'https://api.service.com/health',
'https://cdn.example.com/assets/app.js',
'https://webhook.receiver.com/endpoint',
];
foreach ($urls as $url) {
$status = getHttpStatus($url);
$status === 200
? print("✅ {$url} - OK\n")
: print("❌ {$url} - FAILED ({$status})\n");
}
?>
| 特性 | get_headers() | cURL |
|---|---|---|
| 相依性 | 核心內建 | 需 curl 擴充 |
| 協定支援 | HTTP/HTTPS | HTTP/HTTPS/FTP/SMTP/... |
| 重導向處理 | 自動追蹤 (不可控) | 可配置策略 |
| 超時控制 | 受 default_socket_timeout 影響 | 精確控制連線/執行超時 |
| 自定 Header | 支援 (stream context) | 完整支援 |
| 效能 | 較輕量 | 功能豐富但較重 |
| 錯誤診斷 | 有限 | 詳細錯誤代碼/訊息 |
function checkUrlsConcurrent(array $urls, int $maxConcurrent = 10): array {
$mh = curl_multi_init();
$handles = [];
$results = [];
// 初始化 handles
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
]);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
// 執行並發
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.1); // 等待活動
} while ($running > 0);
// 收集結果
foreach ($handles as $i => $ch) {
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
$results[$urls[$i]] = $error ? false : $httpCode;
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
return $results;
}
// 100 個 URL 並發檢測,僅需單次最慢請求的時間
$results = checkUrlsConcurrent($urls, 20);
| 分類 | 代碼 | 含義 | 處理建議 |
|---|---|---|---|
| 2xx 成功 | 200 | OK | 正常處理 |
| 201 | Created | POST 建立成功 | |
| 204 | No Content | 成功但無回應內容 | |
| 3xx 重導向 | 301 | Moved Permanently | 更新 URL 為 Location |
| 302 | Found | 暫時重導向 | |
| 304 | Not Modified | 快取有效 | |
| 4xx 客戶端錯誤 | 400 | Bad Request | 檢查請求參數 |
| 401 | Unauthorized | 需認證 | |
| 403 | Forbidden | 權限不足 | |
| 404 | Not Found | 資源不存在 | |
| 429 | Too Many Requests | 限流,需退避重試 | |
| 5xx 伺服器錯誤 | 500 | Internal Server Error | 記錄日誌、告警 |
| 502 | Bad Gateway | 上游服務異常 | |
| 503 | Service Unavailable | 暫時不可用,重試 |
雙重方案確保相容性與穩健性:get_headers() 輕量快速,cURL 功能完整可控,生產環境建議以 cURL 為主、get_headers 為備援。