cloudflare 应用例子几则

Cloudflare(以下简称 CF) 堪称互联网活菩萨,造就一大批高质量的互联网基础技术服务,更难得的是,几乎所有的服务都有对应的免费的套餐,而且出手大方,基本上有足够的容量或请求次数。

CDN

CF 最初闻其大名是 DDoS 防护做得好,以及内容分发网络(CDN)。当前 CF 把这些服务统一起来,整合到 Website 网站中,用户只需要把域名的 nameserver 指向 CF管理中,即可享用这些既快速又安全的服务。

在这里插入图片描述
举一个例子,你的网站放在一个小水管的机器中(例如才 1M 带宽),这时就可以通过 CF 的 CDN 来达到加速的目的。虽然 CF 当前并没有针对中国大陆的优化节点,但测试来说,访问速度并不算太慢。而且除了加速的增强外,还有安全、自带 HTTPS 证书等的多种好处。

域名的 nameserver 指向 CF 后,等于以后所有的域名绑定都在 CF 上进行(A 地址、CNAME、TXT 等),相当于 dnspod 的服务。

验证码 Turnstile

Turnstile 用于 CAPTCHA 的验证码服务。不知为何,我调用 Turnstile 总是遇到问题,以至于我不得不放弃 Turnstile 转投谷歌的,甚是遗憾:(

Pages 页面寄存服务

大家都喜欢用 Github Pages 作为博客寄存的服务,国内的 gitee 也有对应的服务,方便简单迅速。但是 Github 的访问总是那么不畅顺,gitee 亲测也是,不太稳定。于是 Cloudflare Pages 也针对 git 发布的页面提供了加速服务,只要你提供 git 的访问权限,即可自动部署发布,域名的话则通过 CNAME 简单绑到 CF。当然你也可以上传静态资源到 CF。

在这里插入图片描述
值得一提的是,Pages 可以整合来自 CF 自己的 Web Analytics,也就是不用加入什么百度统计 Google、Analytics 的 JS 脚本,即可网站的访问统计。同理,开启 CDN 的也能够如法炮制。

在这里插入图片描述

Workers

Cloudflare Workers 适用于多种场景,包括但不限于静态网站托管、API 代理、实时数据处理、Web 应用程序的后端逻辑等。通过 Workers,开发者可以构建快速、可靠且安全的应用程序和服务。

简单的反向代理服务器

下面利用 Workers 做一个反向代理服务器,也就是一个网站的镜像:

// 反代目标网站.
const upstream = 'www.dalao.pro';

// 反代目标网站的移动版.
const upstream_mobile = 'www.dalao.pro';

// 访问区域黑名单(按需设置).
const blocked_region = ['TK'];

// IP地址黑名单(按需设置).
const blocked_ip_address = ['0.0.0.0', '127.0.0.1'];

// 路径替换.
const replace_dict = {
    '$upstream': '$custom_domain',
    '//archiveofourown.org': ''
};

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))
        var upstream_domain = upstream;
    else
        var 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('cache-control', 'public, max-age=14400')
        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;
}

极致的代理

其实,最简单的反向代理是这样的,才几行 js 代码。这是使用 Cloudflare Workers 加速 Oracle Cloud 对象存储。

const REGION = "";
const BUCKET = "";
const NAMESPACE = "";

const BUCKET_URL = `https://objectstorage.${REGION}.oraclecloud.com/n/${NAMESPACE}/b/${BUCKET}/o`;

addEventListener("fetch", (event) => event.respondWith(handle(event)));

async function handle(event) {
  const pathname = new URL(event.request.url).pathname;
  return fetch(`${BUCKET_URL}${pathname}`);
}

Cloudflare Workers 直接转发请求和响应。

发挥 CDN 缓存的作用

缓存模式下,先使用 Cache API 将请求缓存于 Cloudflare 的 CDN ,再返回响应正文。

const REGION = "";
const BUCKET = "";
const NAMESPACE = "";

const BUCKET_URL = `https://objectstorage.${REGION}.oraclecloud.com/n/${NAMESPACE}/b/${BUCKET}/o`;

addEventListener("fetch", (event) => event.respondWith(handle(event)));

async function handle(event) {
  let request = event.request;

  if (["GET", "HEAD", "OPTIONS"].includes(request.method)) {
    const pathname = new URL(request.url).pathname;
    const cache = caches.default;

    let response;
    
    if (request.method === "GET") {
      // 缓存 GET 请求
      response = await cache.match(request);
      
      if (!response) {
        response = await fetch(`${BUCKET_URL}${pathname}`);
        let headers = Object.fromEntries(new Map(response.headers));
        headers["cache-control"] = "public, max-age=14400";
        response = new Response(response.body, { ...response, headers });
        event.waitUntil(cache.put(request, response.clone()));
      }
    } else 
      // 将非 GET 请求直接转发
      response = fetch(`${BUCKET_URL}${pathname}`, { method: request.method });
    
    // override 错误信息以避免泄露 bucket 名称
    if (response.status === 404)
      return new Response("Not found", { status: 404 });

    // 允许浏览器直接播放 .mp4 文件而不触发下载
    if (pathname.endsWith(".mp4")) {
      headers = { "Content-Type": "video/mp4" };
      response = new Response(response.body, { ...response.body, headers });
    }
    
    return response;
  } else 
    return new Response("Method not allowed", { status: 405 });
}

Workers 有很多想象的空间,大家可以尽情利用:)

R2 对象存储

CF 提供 S3 协议的对象存储,是为 R2 也。可以绑定 CNAME 的域名。

在这里插入图片描述
注意,要绑信用卡 or paypal,但是免费。

Email Routing

CloudFlare Email Routing 是一套免费的邮件转发方案,支持Catch-all,可以将任意邮箱转发到任意邮箱。

# catch-all 支持将任意邮箱转发到指定邮箱,并隐藏你的真实邮箱地址
abc@luolei.org -> your-public-mail@gmail.com
edf@luolei.org -> your-public-mail@gmail.com
xyz@luolei.org -> your-public-mail@gmail.com

Cloudflare 的 Workers,支持你根据不同的域名,设置不同的转发规则,比如:

export default {
  async email(message, env, ctx) {
    // 通过 message.to 判断希望转发到哪个邮箱
    // 例如把 ssyy.1984@luoeli.org 转发到 your-secert-mail@gmail.com
    let forwardAddress = message.to.includes(".1984")
      ? "your-secert-mail@gmail.com"
      : "your-public-mail@gmail.com";
    await message.forward(forwardAddress);
  },
};

使用Catch-all方案,理论上可以实现无限多个邮箱地址,有时候我需要注册一些不太重要的境外服务,就会使用这个方案。毕竟网络安全不分国界,境外服务也存在数据泄漏、密码爆破的风险。

结语

CF 还有很多好用的基础服务,就像一个宝藏,等着我们去慢慢发掘。最后,容我说句:CF 谢谢您!

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sp42a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值