搭建鏡像網站

@zgcwkj  2025年06月14日

分類:

代碼 網站 

利用 Cloudflare 的 Workers 反代網站

// 需要反代的網站域名
const upstream = 'example.com';

// 自定義的路徑(可選)
const upstream_path = '/';

// 移動設備訪問時的反代域名(如與PC端一致可省略修改)
const upstream_mobile = 'example.com';

// 禁用服務的國家/地區
const blocked_region = ['CN', 'US'];

// 禁用服務的IP地址
const blocked_ip_address = ['0.0.0.0', '127.0.0.1'];

// 是否啟用HTTPS協議
const https = true;

// 是否禁用緩存
const disable_cache = false;

// 替換文本規則(可根據需求調整)
const replace_dict = {
    '$upstream': '$custom_domain',
    '': ''
};

// 監聽Fetch事件
addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
});

// 主邏輯
async function fetchAndApply(request) {
    const region = request.headers.get('cf-ipcountry')?.toUpperCase();
    const ip_address = request.headers.get('cf-connecting-ip');
    const user_agent = request.headers.get('user-agent');
    let response = null;
    let url = new URL(request.url);
    let url_hostname = url.hostname;

    // 設置協議
    url.protocol = https ? 'https:' : 'http:';

    // 判斷設備類型
    const upstream_domain = await device_status(user_agent) ? upstream : upstream_mobile;

    // 更新路徑和域名
    url.host = upstream_domain;
    url.pathname = url.pathname === '/' ? upstream_path : upstream_path + url.pathname;

    // 區域或IP封鎖
    if (blocked_region.includes(region)) {
        return new Response('訪問被拒絕:此服務暫未覆蓋您的地區。', { status: 403 });
    } else if (blocked_ip_address.includes(ip_address)) {
        return new Response('訪問被拒絕:您的IP已被封禁。', { status: 403 });
    }

    // 設置請求頭
    const method = request.method;
    const new_request_headers = new Headers(request.headers);
    new_request_headers.set('Host', upstream_domain);
    new_request_headers.set('Source', "cloudflare_worker");
    new_request_headers.set('Referer', `${url.protocol}//${url_hostname}`);

    // 處理請求
    const original_response = await fetch(url.href, {
        method,
        headers: new_request_headers
    });

    // WebSocket連接直接返回
    if (new_request_headers.get("Upgrade")?.toLowerCase() === "websocket") {
        return original_response;
    }

    const original_response_clone = original_response.clone();
    let response_headers = new Headers(original_response.headers);
    let original_text = null;

    // 設置響應頭
    if (disable_cache) {
        response_headers.set('Cache-Control', 'no-store');
    }
    response_headers.set('Access-Control-Allow-Origin', '*');
    response_headers.set('Access-Control-Allow-Credentials', 'true');
    ['content-security-policy', 'content-security-policy-report-only', 'clear-site-data'].forEach(header => {
        response_headers.delete(header);
    });

    // 替換文本內容
    const content_type = response_headers.get('content-type');
    if (content_type?.includes('text/html') && content_type.includes('UTF-8')) {
        original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
    } else if (content_type?.includes('application/xml') || content_type?.includes('text/plain')) {
        original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);
    } else {
        original_text = original_response_clone.body;
    }

    // 返回最終響應
    return new Response(original_text, {
        status: original_response.status,
        headers: response_headers
    });
}

// 替換響應文本
async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text();
    for (const [key, value] of Object.entries(replace_dict)) {
        const search = key === '$upstream' ? upstream_domain : key === '$custom_domain' ? host_name : key;
        const replacement = value === '$upstream' ? upstream_domain : value === '$custom_domain' ? host_name : value;
        text = text.replace(new RegExp(search, 'g'), replacement);
    }
    return text;
}

// 判斷設備類型(移動或桌面)
async function device_status(user_agent_info) {
    const agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    return !agents.some(agent => user_agent_info.includes(agent));
}
偷偷說一句,可以實現免備案訪問網站


評論已關閉

Top