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

PHP代理服务器页面跳转重定向问题求助

PHP代理服务器跳转路径修复方案

问题说明

开发用于浏览器嵌套项目的PHP代理服务器时,出现路径跳转错误:

  • 在代理内访问谷歌执行搜索,页面跳转到proxyserver/search?q=hi,而非预期的proxyserver/proxy.php?url=https://google.com/search?q=hi
  • 代理内站点从/pageone跳转至/pagetwo时,实际从proxyserver/proxy.php?url=https://example.com/pageone跳转到proxyserver/pagetwo,丢失代理转发逻辑

核心原因

当前代码直接返回目标站点的原始HTML内容,其中的相对链接、表单提交地址等都是基于目标站点域名的。浏览器解析这些路径时,会默认以代理服务器的根目录为基准,而非通过proxy.php脚本携带目标URL参数转发请求。

修复方案

需要对返回的HTML内容进行全局替换,将所有相对路径转换为通过proxy.php转发的绝对路径,具体步骤:

  1. 解析目标URL的基础URL(协议+域名+端口),用于拼接相对路径
  2. 重写所有<a>标签的href属性
  3. 重写所有<form>标签的action属性
  4. 处理页面内的JavaScript跳转逻辑(如location.href)

修改后的完整代码

ob_start("ob_gzhandler");
ini_set('memory_limit', '128M');
session_start();

$cookieFile = 'cookies.txt';

if (!isset($_COOKIE['PHPSESSID'])) {
    clearCookies();
}

if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['url'])) {
    $url = $_GET['url'];
    proxyRequest($url);
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['url'])) {
    $url = $_POST['url'];
    // 移除POST数据中的url字段,避免提交重复参数
    unset($_POST['url']);
    $postData = http_build_query($_POST);

    if (!filter_var($url, FILTER_VALIDATE_URL)) {
        echo "Error: Invalid URL.";
        exit;
    }

    proxyRequest($url, $postData);
} else {
    echo "Debug: Invalid request received. ";
    echo "Debug: GET Data - " . json_encode($_GET) . "<br>";
    echo "Debug: POST Data - " . json_encode($_POST) . "<br>";

    echo "Error: Invalid request.";
}

function proxyRequest($url, $postData = null) {
    header('Content-Type: text/html');
    header('Access-Control-Allow-Origin: *');

    global $cookieFile;

    $customDNS = [
        'example.com:80:1.1.1.1',
        'example.com:443:8.8.8.8',
    ];

    $ch = curl_init($url);

    // 解析目标URL的基础信息
    $parsedUrl = parse_url($url);
    $baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
    if (isset($parsedUrl['port'])) {
        $baseUrl .= ':' . $parsedUrl['port'];
    }
    $currentPath = isset($parsedUrl['path']) ? $parsedUrl['path'] : '/';
    // 处理当前路径为目录(用于相对路径拼接)
    if (strpos($currentPath, '.') !== false) {
        $currentPath = dirname($currentPath);
    }
    if ($currentPath !== '/') {
        $currentPath .= '/';
    }

    $options = [
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
        CURLOPT_HTTPHEADER => [
            'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'Accept-Language: en-GB',
            'Accept-Encoding: gzip, deflate',
            'Dnt: 1',
            'Sec-GPC: 1',
            'Sec-Fetch-Dest: document',
            'Sec-Fetch-Mode: navigate',
            'Connection: keep-alive',
            'Upgrade-Insecure-Requests: 1',
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_AUTOREFERER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_TIMEOUT => 10,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_RESOLVE => $customDNS,
    ];

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $options[CURLOPT_POST] = true;
        $options[CURLOPT_POSTFIELDS] = $postData;
    }

    if (isset($_COOKIE['PHPSESSID'])) {
        $options[CURLOPT_COOKIEJAR] = $cookieFile;
        $options[CURLOPT_COOKIEFILE] = $cookieFile;
    }

    curl_setopt_array($ch, $options);

    $content = curl_exec($ch);

    if ($content !== false) {
        preg_match_all('/Set-Cookie: (.*?);/i', $content, $matches);
        $receivedCookies = implode('; ', $matches[1]);

        file_put_contents($cookieFile, $receivedCookies);

        // 开始重写内容中的路径
        $proxyScript = $_SERVER['SCRIPT_NAME']; // 获取当前代理脚本路径,如/proxyserver/proxy.php

        // 1. 重写<a>标签的href属性
        $content = preg_replace_callback('/<a[^>]+href=["\'](.*?)["\']/i', function($matches) use ($baseUrl, $currentPath, $proxyScript) {
            $href = $matches[1];
            // 跳过绝对URL、mailto、tel等
            if (preg_match('/^(https?:\/\/|mailto:|tel:|#)/i', $href)) {
                return $matches[0];
            }
            // 处理相对路径
            if (strpos($href, '/') === 0) {
                $fullUrl = $baseUrl . $href;
            } else {
                $fullUrl = $baseUrl . $currentPath . $href;
            }
            // 转换为代理地址
            return str_replace($href, $proxyScript . '?url=' . urlencode($fullUrl), $matches[0]);
        }, $content);

        // 2. 重写<form>标签的action属性
        $content = preg_replace_callback('/<form[^>]+action=["\'](.*?)["\']/i', function($matches) use ($baseUrl, $currentPath, $proxyScript) {
            $action = $matches[1];
            if (empty($action)) {
                // action为空时,默认是当前页面URL
                $fullUrl = $baseUrl . $currentPath;
            } elseif (preg_match('/^(https?:\/\/)/i', $action)) {
                return $matches[0];
            } elseif (strpos($action, '/') === 0) {
                $fullUrl = $baseUrl . $action;
            } else {
                $fullUrl = $baseUrl . $currentPath . $action;
            }
            // 转换为代理地址,同时保留原表单的method
            return str_replace($action, $proxyScript . '?url=' . urlencode($fullUrl), $matches[0]);
        }, $content);

        // 3. 重写JavaScript中的location.href跳转
        $content = preg_replace_callback('/location\.href\s*=\s*["\'](.*?)["\']/i', function($matches) use ($baseUrl, $currentPath, $proxyScript) {
            $href = $matches[1];
            if (preg_match('/^(https?:\/\/)/i', $href)) {
                return $matches[0];
            }
            if (strpos($href, '/') === 0) {
                $fullUrl = $baseUrl . $href;
            } else {
                $fullUrl = $baseUrl . $currentPath . $href;
            }
            return 'location.href = "' . $proxyScript . '?url=' . urlencode($fullUrl) . '"';
        }, $content);

        echo $content;
    } else {
        error_log('Error: Unable to fetch content from the external website. cURL Error: ' . curl_error($ch));

        echo "Error: Unable to fetch content from the external website. cURL Error: " . curl_error($ch);
    }

    curl_close($ch);
}

function clearCookies() {
    global $cookieFile;
    if (file_exists($cookieFile)) {
        unlink($cookieFile);
    }
}
?>

关键修改点

  • 新增目标URL解析逻辑,提取基础域名和当前路径,用于拼接相对路径
  • 添加<a>标签href属性重写,将相对路径转换为代理转发地址
  • 添加<form>标签action属性重写,确保表单提交通过代理处理
  • 添加JavaScript跳转逻辑重写,修正页面内脚本跳转路径
  • 优化POST请求处理,移除POST数据中的url字段,避免参数冗余

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:09:50