基于cloudflare workers搭建在线代理

前言:看到论坛有佬友在玩在线代理,手痒下午花了点时间自己糊了一个。
对于去陌生环境要访问github能解燃眉之急吧
原理:通过target实现
尝试:固定ip失败,动态代理失败
真实ip地址:[2a06:98c0:3600::103]
防被冲烂就不放演示地址了,附上效果图


国内测试ip

代码:

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request));
});

// 处理请求
async function handleRequest(request) {
  const url = new URL(request.url);
  const targetUrl = url.searchParams.get('target');
  const protocol = url.searchParams.get('protocol') || 'https'; // 获取协议参数,默认为'https'
  const region = url.searchParams.get('region') || 'default'; // 获取区域参数,默认为'default'

  if (!targetUrl) {
    // 如果没有提供目标URL,显示一个简单的表单
    return new Response(`
      <!DOCTYPE html>
      <html lang="zh-CN">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Proxy Access Form</title>
        <style>
          body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f4f4f4;
          }
          h1 {
            color: #333;
          }
          form {
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            max-width: 500px;
            margin: 0 auto;
          }
          label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
          }
          input[type="text"], select {
            width: calc(100% - 22px);
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 4px;
          }
          button {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
          }
          button:hover {
            background-color: #0056b3;
          }
        </style>
        <script>
          function updateProtocol() {
            const protocol = document.getElementById('protocol').value;
            const urlInput = document.getElementById('url');
            const value = urlInput.value.trim();

            // 如果用户输入的目标URL没有协议前缀,自动添加所选协议
            if (value && !value.startsWith('http://') && !value.startsWith('https://')) {
              urlInput.value = protocol + '://' + value;
            }
          }

          // 监听表单提交事件
          function handleFormSubmit() {
            const urlInput = document.getElementById('url');
            const protocol = document.getElementById('protocol').value;
            const value = urlInput.value.trim();

            // 确保用户提交的目标URL始终带有协议前缀
            if (value && !value.startsWith('http://') && !value.startsWith('https://')) {
              urlInput.value = protocol + '://' + value;
            }
          }
        </script>
      </head>
      <body>
        <h1>Proxy Access Form</h1>
        <form action="/" method="GET" onsubmit="handleFormSubmit()">
          <label for="protocol">选择协议:</label>
          <select id="protocol" name="protocol" onchange="updateProtocol()">
            <option value="http" ${protocol === 'http' ? 'selected' : ''}>HTTP</option>
            <option value="https" ${protocol === 'https' ? 'selected' : ''}>HTTPS</option>
          </select>
          <label for="url">目标URL:</label>
          <input type="text" id="url" name="target" placeholder="www.example.com" required oninput="updateProtocol()">
          <label for="region">选择区域:</label>
          <select id="region" name="region">
            <option value="us" ${region === 'us' ? 'selected' : ''}>美国</option>
            <option value="eu" ${region === 'eu' ? 'selected' : ''}>欧洲</option>
            <option value="asia" ${region === 'asia' ? 'selected' : ''}>亚洲</option>
            <option value="default" ${region === 'default' ? 'selected' : ''}>默认</option>
          </select>
          <button type="submit">访问</button>
        </form>
      </body>
      </html>
    `, {
      headers: { 'Content-Type': 'text/html' },
    });
  }

  // 构建目标URL
  const targetProtocol = protocol === 'http' ? 'http' : 'https';
  let fullTargetUrl = targetUrl;

  if (!fullTargetUrl.startsWith('http://') && !fullTargetUrl.startsWith('https://')) {
    fullTargetUrl = `${targetProtocol}://${fullTargetUrl}`;
  }

  fullTargetUrl = new URL(fullTargetUrl);

  // 获取主域名
  const targetDomain = getDomainFromUrl(fullTargetUrl.toString());

  // 获取子域名列表
  const subDomains = await getSubDomains(targetDomain);

  // 获取对应区域的IP池
  const proxyIps = getIpPoolForRegion(region);
  const workerUrl = `${url.protocol}//${url.hostname}`;

  try {
    let currentUrl = fullTargetUrl.toString();
    let response;

    // 处理所有重定向,直到没有重定向为止
    while (true) {
      response = await fetchWithRedirectHandling(currentUrl, request, workerUrl, proxyIps);
      const contentType = response.headers.get('Content-Type') || '';
      const isHtml = contentType.includes('text/html');
      const isCss = contentType.includes('text/css');
      const isJson = contentType.includes('application/json');
      const isImage = contentType.includes('image/');

      let responseBody;
      if (isHtml) {
        responseBody = await response.text();
        responseBody = modifyHtmlResponseBody(responseBody, currentUrl, workerUrl, subDomains);
      } else if (isCss) {
        responseBody = await response.text();
        responseBody = modifyCssResponseBody(responseBody, currentUrl, workerUrl);
      } else if (isJson) {
        responseBody = await response.json();
      } else if (isImage) {
        responseBody = await response.arrayBuffer();
      } else {
        responseBody = await response.text(); // 默认处理文本内容
      }

      // 更新CORS头
      const headers = new Headers(response.headers);
      headers.set('Access-Control-Allow-Origin', '*');
      headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
      headers.set('Access-Control-Allow-Headers', '*');

      // 处理响应中的Cookie
      handleCookies(response.headers);

      // 检查是否需要处理重定向
      if ([301, 302, 303, 307, 308].includes(response.status)) {
        const location = response.headers.get('Location');
        if (location) {
          currentUrl = new URL(location, currentUrl).toString();
          // 继续处理新的URL
          continue;
        }
      }

      return new Response(responseBody, {
        status: response.status,
        statusText: response.statusText,
        headers: headers,
      });
    }

  } catch (error) {
    console.error("代理请求失败:", error);
    return new Response(`代理请求失败: ${error.message}`, { status: 500 });
  }
}

// 获取域名
function getDomainFromUrl(url) {
  const { hostname } = new URL(url);
  return hostname.split('.').slice(-2).join('.'); // 取主域名
}

// 获取子域名(模拟)
async function getSubDomains(domain) {
  // 模拟子域名查询,这里可以使用实际的子域名查询服务
  return [
    `sub1.${domain}`,
    `sub2.${domain}`,
    `sub3.${domain}`
  ];
}

// 简化的IP地址池配置
function getIpPoolForRegion(region) {
  const ipPools = {
    'us': ["198.51.100.1", "198.51.100.2", "198.51.100.3"],
    'eu': ["203.0.113.1", "203.0.113.2", "203.0.113.3"],
    'asia': ["192.0.2.1", "192.0.2.2", "192.0.2.3"],
    'default': ["192.0.2.4", "192.0.2.5", "192.0.2.6"]
  };

  return ipPools[region] || ipPools['default'];
}

// 获取代理请求初始化配置
function getProxyInit(originalRequest, proxyIps) {
  const randomIp = proxyIps[Math.floor(Math.random() * proxyIps.length)];

  const init = {
    method: originalRequest.method,
    headers: new Headers(originalRequest.headers),
    redirect: 'manual',
  };

  // 设置代理IP地址作为X-Forwarded-For和CF-Connecting-IP
  init.headers.set('X-Forwarded-For', randomIp);
  init.headers.set('CF-Connecting-IP', randomIp);

  // 模拟正常用户的User-Agent和其他头信息
  init.headers.set('User-Agent', getRandomUserAgent());
  init.headers.set('Referer', originalRequest.url);

  if (originalRequest.method !== "GET" && originalRequest.method !== "HEAD") {
    init.body = originalRequest.body;
  }

  // 将Cookie传递到代理请求中
  const cookies = originalRequest.headers.get('Cookie');
  if (cookies) {
    init.headers.set('Cookie', cookies);
  }

  // 处理Authorization头
  const authorization = originalRequest.headers.get('Authorization');
  if (authorization) {
    init.headers.set('Authorization', authorization);
  }

  return init;
}

// 生成随机User-Agent模拟正常用户行为
function getRandomUserAgent() {
  const userAgents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (Linux; Android 10; SM-A505FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0"
  ];
  return userAgents[Math.floor(Math.random() * userAgents.length)];
}

// 处理请求并跟随重定向
async function fetchWithRedirectHandling(targetUrl, originalRequest, workerUrl, proxyIps) {
  const init = getProxyInit(originalRequest, proxyIps);
  let response = await fetch(targetUrl, init);

  // 处理重定向
  while ([301, 302, 303, 307, 308].includes(response.status)) {
    const location = response.headers.get('Location');
    if (location) {
      targetUrl = new URL(location, targetUrl).toString();
      response = await fetch(targetUrl, init);
    } else {
      break;
    }
  }

  return response;
}

// 修改HTML响应体中的URL
function modifyHtmlResponseBody(body, originalUrl, workerUrl, subDomains) {
  const baseUrl = new URL(originalUrl);

  // 替换HTML中的相对链接
  let updatedBody = body
    .replace(/(href|src)="(?!http|https|mailto|data|\/\/)([^"]*)"/g, (match, p1, p2) => {
      const absoluteUrl = new URL(p2, baseUrl).toString();
      return `${p1}="${workerUrl}?target=${encodeURIComponent(absoluteUrl)}"`;
    })
    .replace(/url\(["']?(?!http|https|mailto|data|\/\/)([^"')]*?)["']?\)/g, (match, p1) => {
      const absoluteUrl = new URL(p1, baseUrl).toString();
      return `url(${workerUrl}?target=${encodeURIComponent(absoluteUrl)})`;
    });

  // 确保所有子域名也被代理
  subDomains.forEach(subDomain => {
    const subDomainUrl = `https://${subDomain}`;
    updatedBody = updatedBody.replace(new RegExp(subDomainUrl, 'g'), `${workerUrl}?target=${encodeURIComponent(subDomainUrl)}`);
  });

  return updatedBody;
}

// 修改CSS响应体中的URL
function modifyCssResponseBody(body, originalUrl, workerUrl) {
  const baseUrl = new URL(originalUrl);

  // 替换CSS中的相对链接
  return body.replace(/url\(["']?(?!http|https|mailto|data|\/\/)([^"')]*?)["']?\)/g, (match, p1) => {
    const absoluteUrl = new URL(p1, baseUrl).toString();
    return `url(${workerUrl}?target=${encodeURIComponent(absoluteUrl)})`;
  });
}

// 处理响应中的Cookie
function handleCookies(headers) {
  const cookies = headers.get('Set-Cookie');
  if (cookies) {
    // 将目标网站设置的Cookie传递到客户端
    const cookieHeader = new Headers();
    cookieHeader.set('Set-Cookie', cookies);
    // 这里可以扩展处理其他的Cookie设置
  }
}

6 个赞

好耶 前排 :xhs_012:

前排 点赞支持