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

PHP cURL 取得 302 重導向後的最終網址

     編輯:PHP 綜合知識

核心概念

HTTP 302 Found 回應會在 Location 標頭指示新位址。cURL 預設**不會**自動追蹤重導向,需啟用 CURLOPT_FOLLOWLOCATION。若需取得最終有效 URL,可透過 curl_getinfo() 的 CURLINFO_EFFECTIVE_URL 取得。

完整函數實作

<?php
/**
 * 發送 POST 請求並取得最終有效 URL (追蹤 302/301 重導向)
 *
 * @param string $url 目標網址
 * @param array|string $postFields POST 欄位資料
 * @param array $options 額外 cURL 選項
 * @return string|false 最終網址或 false(失敗)
 */
function curlPostFollowRedirect($url, $postFields = [], $options = [])
{
    $ch = curl_init();

    $defaultOptions = [
        CURLOPT_URL            => $url,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $postFields,
        CURLOPT_RETURNTRANSFER => true,     // 回傳字串而非直接輸出
        CURLOPT_FOLLOWLOCATION => true,     // 關鍵:自動追蹤重導向
        CURLOPT_MAXREDIRS      => 10,       // 最大重導向次數防迴圈
        CURLOPT_SSL_VERIFYPEER => false,    // 開發環境可關閉驗證,生產請開啟並設定 CA
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_TIMEOUT        => 30,       // 總逾時秒數
        CURLOPT_CONNECTTIMEOUT => 10,       // 連線逾時
        CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP cURL Bot)',
        CURLOPT_HEADER         => false,    // 不包含回應標頭於內容中
    ];

    curl_setopt_array($ch, array_merge($defaultOptions, $options));

    $response = curl_exec($ch);

    if ($response === false) {
        $errno = curl_errno($ch);
        $error = curl_error($ch);
        error_log("cURL Error [$errno]: $error (URL: $url)");
        curl_close($ch);
        return false;
    }

    // 取得最終有效 URL (經過所有重導向後)
    $effectiveUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    curl_close($ch);

    // 可選:根據 HTTP 狀態碼判斷成功
    if ($httpCode >= 400) {
        error_log("HTTP $httpCode from final URL: $effectiveUrl");
        return false;
    }

    return $effectiveUrl;
}

// 使用範例
$finalUrl = curlPostFollowRedirect('https://example.com/login', [
    'username' => 'user',
    'password' => 'pass',
]);

if ($finalUrl) {
    echo "最終導向: $finalUrl";
} else {
    echo "請求失敗";
}
?>

關鍵選項說明

選項用途
CURLOPT_FOLLOWLOCATION啟用自動追蹤 Location 標頭 (301/302/303/307/308)
CURLOPT_MAXREDIRS限制最大重導向次數,防止無限迴圈 (預設 20)
CURLINFO_EFFECTIVE_URL取得最後實際請求的 URL (含重導向後)
CURLOPT_AUTOREFERER自動設定 Referer 標頭於重導向請求中

進階:手動處理重導向 (需保留 Cookie/標頭)

若目標站檢查 Referer 或需手動控制 Cookie,可關閉自動追蹤並自行迴圈:

<?php
function curlManualFollow($url, $postFields = [], $maxRedirects = 10)
{
    $ch = curl_init();
    $cookieJar = tempnam(sys_get_temp_dir(), 'cookie_');

    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $postFields,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => false,   // 關閉自動追蹤
        CURLOPT_HEADER         => true,    // 需要標頭以解析 Location
        CURLOPT_COOKIEJAR      => $cookieJar,
        CURLOPT_COOKIEFILE     => $cookieJar,
        CURLOPT_SSL_VERIFYPEER => false,
    ]);

    $currentUrl = $url;
    $redirectCount = 0;

    do {
        curl_setopt($ch, CURLOPT_URL, $currentUrl);
        $response = curl_exec($ch);

        if ($response === false) break;

        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $headers = substr($response, 0, $headerSize);

        if (!in_array($httpCode, [301, 302, 303, 307, 308])) {
            break; // 非重導向,結束
        }

        // 解析 Location 標頭
        if (preg_match('/^Location:\s*(.+)$/im', $headers, $matches)) {
            $location = trim($matches[1]);
            // 處理相對網址
            $currentUrl = (parse_url($location, PHP_URL_SCHEME) ? $location : rtrim($currentUrl, '/') . '/' . ltrim($location, '/'));
            $redirectCount++;
        } else {
            break; // 無 Location 標頭
        }
    } while ($redirectCount < $maxRedirects);

    $finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
    curl_close($ch);
    @unlink($cookieJar);

    return $finalUrl;
}
?>

注意事項

  • open_basedir / safe_mode:啟用時 CURLOPT_FOLLOWLOCATION 會失效,需改用手動模式
  • HTTPS 憑證:生產環境請設定 CURLOPT_CAINFO 指向 CA 憑證束,勿長期關閉驗證
  • POST 重導向行為:301/302 重導向時,瀏覽器通常會將 POST 改為 GET (RFC 7231),cURL 行為一致。若需保持 POST,請使用 307/308 或手動控制
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved