CDN Fetch 行为的分析

CDN 的Fetch行为经常在一些场景行为会非常迷惑,本文记录了最近碰到的两起因为对Fetch行为不太理解,而导致的配置异常

先说下链路架构

graph TD
  A[用户] --> B[DNS 阿里云]
  B --> C[CDN 腾讯云]
  C --> D[AWS源站Nginx]
  D --> E[EKS Svc]
  E --> F[Pod]

整个环境较为复杂,其中一些细节的地方未展开,比如SVC到Pod 还会经过 istio 的sidecar,本文主要聚焦在CDN与Ng层

腾讯云我们用的是 EdgeOne CDN,后需统称为: EO,先说第一个问题,在上了一个 EO 的边缘函数后,301 跳转被 EO 给缓存了

先说为什么这个场景下 301 不能被 CDN 缓存,比如我们现在站点是:www.abc.com/install,该站点在 ng 上会被自动 redirect(301)到 www.abc.com/install/ 等于自动追加了一个「/」

这么做主要是出于规范的考虑,加上「/」斜杠暗示目录结构(如/blog/post/),用户和爬虫更易理解页面层级;无斜杠易被误判为文件(如/blog/post可能被视为无扩展名文件)68。同时主流CMS(如WordPress)默认生成带斜杠URL,减少配置冲突

但有的时候用户也会忘记加上「/」这个时候就需要一个自动跳转机制,但这样对于同一个页面,你就有两个URL,那么这个页面的权重就会被分散成两个,可以理解成,谷歌分给你的流量是100,如果你只有一个页面,那就全部在这个页面上,但如果两个页面,就会一个页面一半,反而会拉低整体的排名,这不是我们想看到的,所以我们需要让爬虫在访问不带「/」也自动跳转,这样在 GSC 里就只有一条记录

我们在EO上加了一个边缘函数,大概逻辑是往 Header 中注入了一些信息用于做 AB

async function handleRequest(request) {
...
	// 克隆并修改请求头
	const newHeaders = new Headers(request.headers);
	newHeaders.set('X-Geo-Country', countryCode);
	// 创建新请求对象
	const newRequest = new Request(request, {
	headers: newHeaders
	});
	// 回源并获取响应
	const response = await fetch(newRequest);
	//const response = await fetch(newRequest,{redirect:'manual'});
	// 将地理位置注入响应头(供客户端读取)
	response.headers.set('X-Geo-Country', countryCode);
	response.headers.set('Access-Control-Expose-Headers', 'X-Geo-Country');
	...
	return response;
}

可以注意到,当我们把Header修改后,需要手工发起一次回源,但是Fetch行为标准有 3 个值:

  • Follow:自动跟随跳转,如果碰到 301 POST,会降级到 GET
  • manual:返回响应,不自动跳转,交给客户端决定
  • error:直接抛出错误,不返回响应 默认是 Follow ,会自动跟随跳转,把源站的数据缓存在CDN,下一次请求,就不会跳转了,会直接命中。

二、301/302 Post 转Get问题

早期网站大量依赖 302 将 POST 转为 GET,突然改变会破坏数百万网站。 解决方法也是把 Follow 改成 manual,让浏览器发起访问。