惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

又耳笔记

用AI挣钱之AI建站:二维码生成器工具 用AI挣钱之AI建站:https://markdowntopdf.top Cloudflare Workers实战番外一:用Static Assets托管网站,告别传统方案 Cloudflare Workers实战(五):不止JavaScript,拥抱Python与Rust Cloudflare Workers实战(四):托管和分发静态文件 Cloudflare Workers实战(三):实现认证、重定向与缓存 Cloudflare Workers实战(一):随心所欲操作客户端请求 Cloudflare workers不完全指南 使用n8n创作短篇小说 水果风波:信任的代价 用Python将PDF文件转换成图片 rust网络框架Pingora源码阅读3 rust网络框架Pingora源码阅读2 rust网络框架Pingora源码阅读1 Pingora快速入门教程1之总览 Rust模板引擎askama快速入门引擎 使用Loco快速搭建自己的后台系统 用Rust发一封图文并茂的邮件 Rust小项目: 写一个简单的恶意流量阻断器 Rust小项目: 写一个简单的网页爬虫 Rust命令行库Clap快速入门教程 Rust小项目:用Rust写一个端口扫描器 Rust真全栈开发快速入门 rust声明宏快速入门教程 Rust文本处理快速入门教程 RUST web框架axum快速入门教程6之测试 白嫖免费的Rust在线运行时shuttle RUST web框架axum快速入门教程5之中间件 用Rust来做以太坊开发5之事件日志及签名 《英语语法分解大全》读后感
Cloudflare Workers实战(二):动态修改后端响应
About 又耳宁 关于技术与股票的一些零碎想法 · 2025-07-17 · via 又耳笔记

之前的文章:

  1. Cloudflare workers不完全指南"
  2. Cloudflare Workers实战(一):随心所欲操作客户端请求

书接上回,在上一篇文章里,咱们聊了怎么在请求到达服务器之前,像个“中间人”一样对它“动手动脚”。这次,咱们把视角翻转一下,看看当服务器把响应(Response)辛辛苦苦准备好,准备发回给浏览器时,我们又能怎么在半路上把它截胡,然后来一波骚操作。有了它,你就能对响应内容进行实时、动态的修改。想想看,这意味着什么?

操作响应有啥用?

你可能会问,后端返回啥,我给用户看不就完了,折腾个啥?此言差矣!修改后端响应的能力,简直是给老系统“微创手术”的神技,有时候甚至是“无创”!你根本不用去动那些陈年老代码,就能玩出新花样。比如:

  • 想给网站加个统计? 简单!在每个HTML页面返回前,刷刷刷,自动给你注入一段Google Analytics或者其他统计脚本。什么客服插件、A/B测试,同理!
  • 后端API太啰嗦? 小事一桩!后端返回一大坨JSON,里面有好多敏感信息或者前端根本用不上的字段?在边缘用Worker裁掉它,只给前端最清爽的数据。甚至还能把好几个API的结果拼在一起,伪装成一个API。
  • 想做内容本地化? 安排!根据访问用户的IP地理位置(Cloudflare会把这个信息放在request.cf.country里),动态修改页面内容,给美国用户看英文,给中国用户看中文,是不是很贴心?不过修改响应显然不是一个好主意,更好的主意应该是在请求后端前就重定向到不同的路径,比如/cn或者/us等

是不是很吊?

拿到响应并“偷窥”一下

要想操作响应,你得先拿到它。在Workers里,这事儿通常靠fetch(request)搞定。这个函数会把原始请求发出去,然后带回来一个标准的Response对象

// worker.js
export default {
  async fetch(request, env, ctx) {
    // 1. 将原始请求发往源站
    const originalResponse = await fetch(request);

    // 2. 读取响应状态码和响应头
    const statusCode = originalResponse.status; // 200, 404, 500 etc.
    const contentType = originalResponse.headers.get('Content-Type') || '';

    console.log(`后端响应状态码: ${statusCode}`);
    console.log(`后端响应类型: ${contentType}`);

    // 3. 为了演示,我们先直接返回原始响应
    return originalResponse;
  },
};

这里有个关键点,和Request对象一样,Response对象的body也是一个可读流(ReadableStream)。这玩意儿就像一次性的磁带,只能被读取一次!读完了就没了。所以,如果你想对响应体做点啥,就得先把它读出来,然后重新组装一个新的响应。

修改响应内容

改造响应的核心思想就一句话:“复制、修改、替换”。拿到原始响应,别直接在上面改,而是以它为蓝本,创建一个全新的、修改过的Response对象,最后把这个新的响应返回给浏览器。

示例一:给响应“贴标签”——修改响应头

这个最简单也最常用。比如,咱们想给所有从服务器返回的HTML页面,都强制加上一个严格的内容安全策略(CSP)头,增强网站的安全性。

// worker.js
export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);
    const contentType = response.headers.get('Content-Type') || '';

    // 只对HTML响应进行操作
    if (contentType.includes('text/html')) {
      // 1. 克隆原始响应,这样我们才能修改它的Headers
      const newResponse = new Response(response.body, response);

      // 2. 设置新的安全头
      newResponse.headers.set(
        'Content-Security-Policy',
        "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none';"
      );
      newResponse.headers.set('X-Content-Type-Options', 'nosniff');

      return newResponse;
    }

    return response;
  },
};

示例二:给HTML页面“做手术”—— HTMLRewriter

如果只是改改响应头,那还不够刺激。真正的大杀器是修改响应体!特别是修改HTML。但HTML通常很大,如果整个读到内存里再修改,那Worker非得累死不可(虽然说跟我们免费用户没啥关系,但是太慢了也不行是不)。所以,Cloudflare给我们提供了一个神器:HTMLRewriter

这玩意儿牛逼之处在于,它是个流式解析器。你可以把它想象成一个在HTML代码流过的管道上的智能机器人,你给它设定好规则(比如“找到所有的<img>标签”),它就在代码流过的时候,实时地对匹配到的部分进行操作,整个过程根本不需要把完整的HTML加载到内存里。性能自然比等响应全部达到之后在处理快很多。

实战一下:咱们就用它来实现刚才说的,在所有HTML页面的</body>标签前,自动注入Google Analytics的跟踪代码。

// worker.js

// 定义一个处理类,用于注入脚本
class AnalyticsInjector {
  constructor(analyticsScript) {
    this.analyticsScript = analyticsScript;
  }

  // 当匹配到 </body> 元素时,在其前面追加内容
  element(element) {
    element.before(this.analyticsScript, { html: true });
  }
}

export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);

    // Google Analytics 脚本
    const gaScript = `
      <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
      <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'G-XXXXXXXXXX');
      </script>`;

    // 使用HTMLRewriter进行转换
    return new HTMLRewriter()
      .on('body', new AnalyticsInjector(gaScript))
      .transform(response);
  },
};

示例三:给JSON数据“瘦身”

现在前后端分离,API返回JSON是家常便饭。同样的,我们也可以在边缘对JSON进行修改。

实战一下:假设有个API返回了用户的所有信息,但我们不希望把用户的电话phone和地址address这种敏感信息暴露给前端

别问后端为啥改,问就是需求太满,没时间做!!!

// worker.js
export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);
    const contentType = response.headers.get('Content-Type') || '';

    if (contentType.includes('application/json')) {
      // 1. 读取原始JSON响应体并解析
      // 因为响应体只能读一次,所以我们克隆一下,以防后续需要原始响应
      const originalData = await response.clone().json();

      // 2. 过滤敏感字段
      const filteredData = {
        id: originalData.id,
        name: originalData.name,
        email: originalData.email,
      };

      // 3. 创建一个新的JSON响应
      return new Response(JSON.stringify(filteredData, null, 2), {
        headers: {
          'Content-Type': 'application/json;charset=UTF-8',
        },
      });
    }

    return response;
  },
};

示例三:无中生有!

有时候我们根本不需要理会后端服务器。Worker自己就能凭空捏造一个响应出来。这在很多场景下特别有用,比如做一个健康检查的接口,或者直接从缓存里返回内容,或者处理一些非常简单的API请求,根本没必要去打扰后端老大哥。

实战一下:咱们来实现一个GET /health的接口,它会返回一个JSON,告诉别人“我还活着”。

用来监控网站健康状态不错,但是因为不直接请求后端,其实可能造成"假健康"。

// worker.js
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // 实现一个简单的健康检查接口
    if (url.pathname === '/health') {
      const responseData = { status: 'ok', timestamp: new Date().toISOString() };
      return new Response(JSON.stringify(responseData), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 其他请求正常转发
    return fetch(request);
  },
};

小结

今天我们了解了Cloudflare Workers的响应操作能力,这让我们拥有了在边缘动态“捏造”响应的超能力。总结一下核心知识点:

  1. 响应只能读一次response.body是可读流,操作前记得clone()是个好习惯。
  2. 修改响应靠新建:通过new Response(body, init)来创建修改后的响应。
  3. 修改HTML用神器HTMLRewriter是处理HTML响应的不二之选,性能高,使用方便。
  4. JSON操作很简单:先.json()解析成对象,改完再JSON.stringify()回去。
  5. 可以无中生有:Worker可以直接new Response()创建全新的响应,无需请求源站。

掌握了请求和响应的“增删改查”,你就已经掌握了Cloudflare Workers最核心、最强大的能力。毫不夸张地说,你能用它实现半个API网关的功能了。

当然,Workers的能耐远不止于此。