前言:看到论坛有佬友在玩在线代理,手痒下午花了点时间自己糊了一个。
对于去陌生环境要访问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设置
}
}