You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WordPress中用PHP追踪单个URL的所有重定向

WordPress重定向追踪功能故障排查与修复

问题背景

在WordPress开发的Web应用中,需实现接收URL并列出所有重定向记录的功能。尝试过curl、wp_remote_head、get_header及file_get_contents等方法,效果不佳,还遇到部分站点返回的header中location参数大小写不一致的问题。

功能通过Ajax调用实现,结果存入transient防止请求轰炸,最终以无序列表展示结果,但当前无法列出重定向记录,始终显示“未找到重定向”。

期望输出格式:

<ul>
<li>Origin: <a href="http://marketingtechblog.com" rel="nofollow noreferrer">http://marketingtechblog.com</a></li>
<li>Redirect (301): <a href="http://martech.zone" rel="nofollow noreferrer">http://martech.zone</a></li>
<li>Redirect (301): <a href="https://martech.zone" rel="nofollow noreferrer">https://martech.zone</a></li>
<li>Destination (200): <a href="https://martech.zone" rel="nofollow noreferrer">https://martech.zone</a></li>
</ul>

当前问题代码

function traceredirects() {
    check_ajax_referer( 'traceredirects_nonce', 'nonce' );
    $url = $_POST['url'];

    $valid_url = filter_var($url, FILTER_VALIDATE_URL);
    if (!$valid_url) {
        echo '<strong>Error:</strong> Please enter a valid URL with http:// or https://';
        wp_die();
    }

    $host = parse_url($url, PHP_URL_HOST);
    if(!checkdnsrr($host, 'ANY')) {
        echo '<strong>Error:</strong> Could not find the domain ' . $host;
        wp_die();
    }

    $transient_name = $url . '_traceredirect';
    $traced = get_transient( $transient_name );

    if (false === $traced) {
        $args = array(
            'redirection' => 10, // follow up to 10 redirects
        );

        $redirects = array();
        $redirects[] = $url;
        $response = wp_remote_get( $url, $args );

        $headers = wp_remote_retrieve_headers( $response );
        $http_code = wp_remote_retrieve_response_code( $response );

        $traced = '<ul>';

        if ( ! is_wp_error( $response ) ) {
            if (!isset($headers['location'])) {
                $traced .= '<li>No redirects found</li>';
            } else {
                while (isset($headers['location'])) {
                    if ( in_array( $headers['location'], $redirects ) ) {
                        echo 'Error: Redirect loop detected';
                        break;
                    }
                    $redirects[] = $headers['location'];
                    $response = wp_remote_get( $headers['location'], $args );
                    $headers = wp_remote_retrieve_headers( $response );
                    $http_code = wp_remote_retrieve_response_code( $response );
                    $traced .= '<li>HTTP code: ' . $http_code . ', URL: ' . $headers['location'] . '</li>';
                }
            }   
        } else {
            echo 'Error: ' . $response->get_error_message();
            wp_die();
        }

        $traced .= '</ul>';
        set_transient( $transient_name, $traced, HOUR_IN_SECONDS );
    }
    echo $traced;
    wp_die();
    }
    add_action( 'wp_ajax_traceredirects', 'traceredirects' );
    add_action( 'wp_ajax_nopriv_traceredirects', 'traceredirects' );

问题分析

  1. 自动跟随重定向导致丢失中间记录:wp_remote_get的redirection参数设为10时,WordPress会自动跟随所有重定向,直接返回最终响应,无法获取中间重定向的header信息。
  2. Location头大小写不兼容:部分站点返回Location(大写开头)而非location,原代码仅检查小写键,导致无法识别重定向地址。
  3. 逻辑漏洞:循环中未处理最终响应的状态,且输出格式不符合期望,同时未处理相对路径的Location地址。

修复后的代码

function traceredirects() {
    check_ajax_referer( 'traceredirects_nonce', 'nonce' );
    $url = $_POST['url'];

    $valid_url = filter_var($url, FILTER_VALIDATE_URL);
    if (!$valid_url) {
        echo '<strong>Error:</strong> 请输入包含http://或https://的有效URL';
        wp_die();
    }

    $host = parse_url($url, PHP_URL_HOST);
    if(!checkdnsrr($host, 'ANY')) {
        echo '<strong>Error:</strong> 无法找到域名 ' . $host;
        wp_die();
    }

    $transient_name = md5($url) . '_traceredirect'; // 用md5避免URL特殊字符导致的键名问题
    $traced = get_transient( $transient_name );

    if (false === $traced) {
        $args = array(
            'redirection' => 0, // 禁止自动跟随,手动处理每一步重定向
            'timeout' => 10,
        );

        $redirect_chain = array();
        $current_url = $url;
        $max_redirects = 10;
        $redirect_count = 0;
        $has_error = false;

        // 记录原始URL
        $redirect_chain[] = array(
            'url' => $current_url,
            'code' => null,
            'type' => 'origin'
        );

        while ($redirect_count < $max_redirects) {
            $response = wp_remote_get( $current_url, $args );

            if ( is_wp_error( $response ) ) {
                $traced = '<ul><li><strong>Error:</strong> ' . $response->get_error_message() . '</li></ul>';
                $has_error = true;
                break;
            }

            $http_code = wp_remote_retrieve_response_code( $response );
            $headers = wp_remote_retrieve_headers( $response );
            
            // 统一将header键转为小写,解决大小写不一致问题
            $lowercase_headers = array_change_key_case( (array)$headers, CASE_LOWER );
            $location = isset($lowercase_headers['location']) ? $lowercase_headers['location'] : null;

            // 处理相对路径的Location地址
            if ($location && !filter_var($location, FILTER_VALIDATE_URL)) {
                $current_parsed = parse_url($current_url);
                $location = $current_parsed['scheme'] . '://' . $current_parsed['host'] . rtrim($current_parsed['path'], '/') . '/' . ltrim($location, '/');
            }

            if ($http_code >= 300 && $http_code < 400 && $location) {
                // 记录重定向步骤
                $redirect_chain[] = array(
                    'url' => $location,
                    'code' => $http_code,
                    'type' => 'redirect'
                );

                // 检测重定向循环
                if (in_array($location, array_column($redirect_chain, 'url'))) {
                    $traced = '<ul><li><strong>Error:</strong> 检测到重定向循环</li></ul>';
                    $has_error = true;
                    break;
                }

                $current_url = $location;
                $redirect_count++;
            } else {
                // 记录最终目的地
                $redirect_chain[] = array(
                    'url' => $current_url,
                    'code' => $http_code,
                    'type' => 'destination'
                );
                break;
            }
        }

        if (!$has_error) {
            $traced = '<ul>';
            foreach ($redirect_chain as $item) {
                $url_html = '<a href="' . esc_url($item['url']) . '" rel="nofollow noreferrer">' . esc_html($item['url']) . '</a>';
                switch ($item['type']) {
                    case 'origin':
                        $traced .= '<li>Origin: ' . $url_html . '</li>';
                        break;
                    case 'redirect':
                        $traced .= '<li>Redirect (' . $item['code'] . '): ' . $url_html . '</li>';
                        break;
                    case 'destination':
                        $traced .= '<li>Destination (' . $item['code'] . '): ' . $url_html . '</li>';
                        break;
                }
            }
            $traced .= '</ul>';
        }

        set_transient( $transient_name, $traced, HOUR_IN_SECONDS );
    }
    echo $traced;
    wp_die();
}
add_action( 'wp_ajax_traceredirects', 'traceredirects' );
add_action( 'wp_ajax_nopriv_traceredirects', 'traceredirects' );

修复说明

  • 将redirection参数设为0,禁止自动跟随重定向,确保能获取每一步的响应头信息。
  • 使用array_change_key_case统一处理header键的大小写,解决Location头大小写不一致问题。
  • 增加相对路径转绝对URL的逻辑,避免无法识别相对地址的重定向。
  • 重构重定向链记录逻辑,生成完全符合期望格式的HTML输出。
  • 用md5处理transient名称,避免URL中特殊字符导致的键名异常。

内容的提问来源于stack exchange,提问作者douglaskarr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:40:35