首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 獲取 301/302 重定向最終 URL:基於 Socket HEAD 請求的輕量級實現

     編輯:PHP 相關知識

不依賴 cURL、allow_url_fopen,純 PHP Socket 發送 HEAD 請求解析 Location Header,支援相對/絕對路徑重定向、多層跳轉檢測與循環保護。

核心函數:單次重定向解析

/**
 * 獲取 URL 的直接重定向目標
 *
 * @param string $url 完整 URL(含 scheme)
 * @return string|false 重定向目標 URL,無重定向或失敗返回 false
 */
function get_redirect_url(string $url): string|false {
    $parts = @parse_url($url);
    if (!$parts || !isset($parts['host'])) return false;
    $parts['path'] = $parts['path'] ?? '/';
    $port = $parts['port'] ?? 80;
    $scheme = $parts['scheme'] ?? 'http';

    // 建立 Socket 連接
    $fp = @fsockopen($parts['host'], $port, $errno, $errstr, 10);
    if (!$fp) return false;

    // 發送 HEAD 請求
    $request = "HEAD {$parts['path']}" . (isset($parts['query']) ? "?{$parts['query']}" : "") . " HTTP/1.1\r\n";
    $request .= "Host: {$parts['host']}\r\n";
    $request .= "Connection: Close\r\n\r\n";
    fwrite($fp, $request);

    // 讀取響應頭
    $response = '';
    while (!feof($fp)) {
        $response .= fread($fp, 8192);
    }
    fclose($fp);

    // 解析 Location
    if (preg_match('/^Location:\s*(.+)$/mi', $response, $m)) {
        $location = trim($m[1]);
        // 處理相對路徑
        if ($location[0] === '/') {
            return "{$scheme}://{$parts['host']}{$location}";
        }
        // 處理相對路徑(無 scheme)
        if (strpos($location, '://') === false && $location[0] !== '/') {
            $base = rtrim("{$scheme}://{$parts['host']}{$parts['path']}", '/');
            return $base . '/' . ltrim($location, '/');
        }
        return $location;
    }
    return false;
}

遞歸追蹤所有重定向

/**
 * 追蹤完整重定向鏈
 *
 * @param string $url 起始 URL
 * @param int $max_depth 最大跳轉層數,防無限循環
 * @return array 重定向鏈 [url1, url2, ...]
 */
function get_all_redirects(string $url, int $max_depth = 10): array {
    $chain = [];
    $visited = [];

    for ($i = 0; $i < $max_depth; $i++) {
        if (in_array($url, $visited, true)) break; // 循環檢測
        $visited[] = $url;

        $next = get_redirect_url($url);
        if (!$next) break;

        $chain[] = $next;
        $url = $next;
    }
    return $chain;
}

使用範例

$start = 'https://example.com/short-link';
$chain = get_all_redirects($start);

echo "起始: $start\n";
foreach ($chain as $i => $u) {
    echo "  {$i}. $u\n";
}
echo "最終: " . ($chain ? end($chain) : $start) . "\n";

// 輸出示例
// 起始: https://example.com/short-link
//   0. https://example.com/real-page
//   1. https://target.com/final?from=example
// 最終: https://target.com/final?from=example

進階增強:支援 HTTPS

function get_redirect_url_ssl(string $url): string|false {
    $parts = @parse_url($url);
    if (!$parts || !isset($parts['host'])) return false;

    $scheme = $parts['scheme'] ?? 'http';
    $host = $parts['host'];
    $port = $parts['port'] ?? ($scheme === 'https' ? 443 : 80);
    $target = ($scheme === 'https' ? 'ssl://' : 'tcp://') . $host . ":{$port}";

    $fp = @fsockopen($target, $port, $errno, $errstr, 10);
    if (!$fp) return false;

    // 其餘邏輯同 HTTP,注意 Host header 需包含端口(非標準端口時)
    $host_header = $port != ($scheme === 'https' ? 443 : 80) ? "$host:$port" : $host;
    // ...
}

對比:cURL 版本(生產環境推薦)

function get_redirect_curl(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_NOBODY => true,      // HEAD
        CURLOPT_FOLLOWLOCATION => false, // 手動處理
        CURLOPT_HEADER => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_SSL_VERIFYPEER => false, // 視安全策略
    ]);
    $resp = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($code >= 300 && $code < 400) {
        if (preg_match('/^Location:\s*(.+)$/mi', $resp, $m)) {
            return trim($m[1]);
        }
    }
    return false;
}

選型建議

方案優點缺點適用場景
Socket HEAD零依賴、輕量、可控需手動處理 HTTPS、Chunked、壓縮、重試受限環境、簡單爬蟲、教學
cURL完整協議支援、高性能、成熟需安裝擴展生產環境、複雜重定向、生產爬蟲
get_headers()一行代碼無法禁用自動跳轉、無超時控制、錯誤處理弱快速腳本、調試

常見坑點

  • 相對路徑重定向:Location: /new-path 或 Location: ../other 需基於當前 URL 解析;
  • 大小寫不敏感:Header 名 Location/location 均可能;
  • 多重 Location:部分服務器錯誤返回多個,取第一個;
  • 編碼問題:URL 中文需 rawurlencode;
  • 無限循環:A→B→A,必須記錄已訪問 URL 並限制深度。

掌握底層 Socket 實現能深入理解 HTTP 重定向機制,生產環境請優先使用成熟的 cURL 或 Guzzle。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved