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层
一、错误缓存301状态码
腾讯云我们用的是 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 里就只有一条记录
1.1 EO为什么会缓存301
我们在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,让浏览器发起访问。