虽然有代理工具能帮助我们访问不存在的网站,但是这终究还是多了一个步骤,挂上代理,而购买/自建代理还可能产生一笔费用(尽管不贵)

如果你只有简单的需求,比如只是单纯的想要一个广告更少、搜索结果更精确的搜索引擎,不妨试试自建一个镜像站

什么是cloudflare workers

cloudflare就不多bb了,业界大佬,提供了很多方便开发者的白嫖免费服务,workers就是其中之一

用他们的话说就是:

Cloudflare Workers provides a serverless execution environment that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure.

前提条件

  1. 有一个cloudflare的账号
  2. 有一个域名,并交给cloudflare解析(可选,但推荐)

假如我想镜像Google

下面将按照你是否有域名两种情况说明

如果你只满足条件1

  1. 在你登陆后的管理界面里找到workers,并点击它
  2. 第一次使用worker,会提示你创建一个子域,子域无法更改,子域格式为:xxx.username.workers.dev
  3. 点击create a worker,之后你会看到一个js编辑器,别慌不用你写代码,将下面的代码段复制进去即可
  4. 点击run,在右侧的显示框中如果能正常显示你想镜像的站点则再点击save and deploy
  5. 等待生效,一般5分钟之内就能完成

如果你同时满足条件1、2

  1. 在你登陆后的管理界面里找到你给cloudflare解析的域名
  2. 在导航菜单上找到workers
  3. 第一次使用worker,会提示你创建一个子域,子域无法更改,子域格式为:xxx.workers.dev
  4. 点击launch editor,之后再点击add script,填入你给镜像站起的名字,然后你会看到一个js编辑器,别慌不用你写代码,将下面的代码段复制进去即可
  5. 点击save,回退,点击add router
  6. router填写https://你的域名/*,worker选择你刚刚新建的worker
  7. 在dns中为域名添加一条cname解析,解析到 worker的名字.xxx.workers.dev
  8. 再点击launch editor,选择新建的worker,点击deploy
  9. 等待生效,一般5分钟之内就能完成

代码段


// 替换成你想镜像的站点
const upstream = 'www.google.com'

// 如果那个站点有专门的移动适配站点,否则保持和上面一致
const upstream_mobile = 'www.google.com'

// 你希望禁止哪些国家访问
const blocked_region = ['KP', 'SY', 'PK', 'CU']

// 禁止自访问
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']

// 替换成你想镜像的站点
const replace_dict = {
    '$upstream': '$custom_domain',
    '//google.com': ''
}

//以下内容都不用动
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_host = url.host;

    if (url.protocol == 'http:') {
        url.protocol = 'https:'
        response = Response.redirect(url.href);
        return response;
    }

    if (await device_status(user_agent)) {
        upstream_domain = upstream
    } else {
        upstream_domain = upstream_mobile
    }

    url.host = upstream_domain;

    if (blocked_region.includes(region)) {
        response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
            status: 403
        });
    } else if(blocked_ip_address.includes(ip_address)){
        response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
            status: 403
        });
    } else{
        let method = request.method;
        let request_headers = request.headers;
        let new_request_headers = new Headers(request_headers);

        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Referer', url.href);

        let original_response = await fetch(url.href, {
            method: method,
            headers: new_request_headers
        })

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

        new_response_headers.set('access-control-allow-origin', '*');
        new_response_headers.set('access-control-allow-credentials', true);
        new_response_headers.delete('content-security-policy');
        new_response_headers.delete('content-security-policy-report-only');
        new_response_headers.delete('clear-site-data');

        const content_type = new_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_host);
        } else {
            original_text = original_response_clone.body
        }

        response = new Response(original_text, {
            status,
            headers: new_response_headers
        })
    }
    return response;
}

async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()

    var i, j;
    for (i in replace_dict) {
        j = replace_dict[i]
        if (i == '$upstream') {
            i = upstream_domain
        } else if (i == '$custom_domain') {
            i = host_name
        }

        if (j == '$upstream') {
            j = upstream_domain
        } else if (j == '$custom_domain') {
            j = host_name
        }

        let re = new RegExp(i, 'g')
        text = text.replace(re, j);
    }
    return text;
}

async function device_status (user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    var flag = true;
    for (var v = 0; v < agents.length; v++) {
        if (user_agent_info.indexOf(agents[v]) > 0) {
            flag = false;
            break;
        }
    }
    return flag;
}

参考

github-WorkersProxy


Keeping frank is the easiest way to keep it simple.