在爬蟲、鏈接驗證、圖片防盜鏈等場景,需快速判斷 URL 是否可訪問(返回 200),避免下載大文件浪費帶寬。PHP 提供兩種原生方案:get_headers() 與 cURL HEAD 請求。
<?php
function urlExistsGetHeaders(string $url): bool {
// @ 抑制警告,allow_url_fopen=On 時可用
$headers = @get_headers($url, true);
if ($headers === false) return false;
// 支援 HTTP/1.1 200 OK、HTTP/2 200 等格式
$statusLine = $headers[0] ?? '';
return (bool)preg_match('~^HTTP/\d\.\d\s+2\d\d~', $statusLine);
}
// 用法
var_dump(urlExistsGetHeaders('https://example.com/image.jpg')); // true/false
優點:代碼極短、無需 cURL 擴展。
缺點:受 allow_url_fopen 限制;無法精細控制超時、代理、SSL 驗證、重定向策略;某些 CDN 返回 403/302 導致誤判。
<?php
function urlExistsCurl(string $url, int $timeout = 5): bool {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true, // HEAD 請求
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true, // 跟隨重定向
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'LinkChecker/1.0',
]);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$errno = curl_errno($ch);
curl_close($ch);
if ($errno) return false; // 網絡層失敗
return $httpCode >= 200 && $httpCode < 300; // 2xx 視為存在
}
優點:完全可控超時、代理、證書、重定向、User-Agent;支援 FTP/HTTP/HTTPS 協議;可獲取 Content-Length、Content-Type 等元信息。
缺點:依賴 cURL 擴展;代碼稍長。
function batchCheck(array $urls, int $concurrency = 20): array {
$mh = curl_multi_init();
$handles = [];
$results = [];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 5,
]);
$handles[$i] = $ch;
curl_multi_add_handle($mh, $ch);
}
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.05);
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$idx = array_search($ch, $handles, true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$errno = curl_errno($ch);
$results[$urls[$idx]] = ($errno === 0 && $code >= 200 && $code < 300);
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running);
curl_multi_close($mh);
return $results;
}
| 現象 | 原因 | 對策 |
|---|---|---|
| 返回 403 Forbidden | 防盜鏈/UA 檢查 | 設置 CURLOPT_REFERER、CURLOPT_USERAGENT 模擬瀏覽器 |
| 返回 301/302 重定向 | 短鏈接/HTTPS 強跳 | 開啟 CURLOPT_FOLLOWLOCATION,檢查最終 CURLINFO_EFFECTIVE_URL |
| 超時/連接重置 | 防火牆/目標限流 | 降低併發、增加超時、使用代理池 |
| HEAD 不支援返回 405 | 服務端禁用 HEAD | 改用 CURLOPT_CUSTOMREQUEST => 'GET' + CURLOPT_RANGE => '0-0' 僅讀 1 字節 |
allow_url_fopen:用 get_headers()。