feat: favicon 边缘缓存 + CDN 整站刷新兜底 + 评论孤儿修复归档
- blog-admin tools.ts: favicon 响应加 s-maxage/CDN-Cache-Control, 让 CF 边缘可缓存,边缘命中即不进 Worker,KV 读取量大幅下降 - scripts/refresh_cdn.js: 裸域名自动补尾斜杠(多吉云 rtype=path 是目录刷新, https://usj.cc 只刷首页,https://usj.cc/ 才是全站),并打印刷新清单 - 新增文档:评论孤儿修复方案、函数版证书管家方案、诊断报告
This commit is contained in:
1 parent
d273330e10
commit
c2f890a0f7
5 files changed
+790
-28
No files matched your search
@@ -75,6 +75,28 @@ async function discoverFaviconUrl(siteUrl: string): Promise<string | null> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* favicon 图片响应的统一缓存头。
|
||||
*
|
||||
* ★ 为什么要显式加 s-maxage / CDN-Cache-Control:
|
||||
* `Cache-Control: max-age=86400` 只让**浏览器**缓存,Cloudflare 边缘默认
|
||||
* **不缓存 Worker 的响应**(实测响应里没有 cf-cache-status)。于是每个访客、
|
||||
* 每次清缓存、每换设备,都会真正进 Worker → 第一条语句就是 KV.get → KV 读量被放大。
|
||||
*
|
||||
* 加上 s-maxage(共享缓存)后,CF 边缘可缓存 24h:边缘命中就完全不进 Worker,
|
||||
* KV 读取 ≈ 0,同时首包更快。浏览器侧仍保留 max-age。
|
||||
* 只对**成功拿到图片**的响应使用;失败/兜底响应不缓存(避免把错误固化一天)。
|
||||
*/
|
||||
function imageCacheHeaders(contentType: string): Record<string, string> {
|
||||
return {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=86400, s-maxage=86400',
|
||||
'CDN-Cache-Control': 'max-age=86400',
|
||||
'Cloudflare-CDN-Cache-Control': 'max-age=86400',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
};
|
||||
}
|
||||
|
||||
export async function favicon(request: Request, env: Env): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const siteUrl = url.searchParams.get('url');
|
||||
@@ -87,14 +109,7 @@ export async function favicon(request: Request, env: Env): Promise<Response> {
|
||||
if (cached) {
|
||||
const [meta, b64] = cached.split('|');
|
||||
const body = Uint8Array.from(atob(b64), (ch) => ch.charCodeAt(0));
|
||||
return new Response(body, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': meta,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
return new Response(body, { status: 200, headers: imageCacheHeaders(meta) });
|
||||
}
|
||||
|
||||
// 失败短缓存:抓取失败的站点 1 小时内不再反复打外站
|
||||
@@ -150,14 +165,7 @@ export async function favicon(request: Request, env: Env): Promise<Response> {
|
||||
}
|
||||
await env.RSS_KV.put(bodyKey, `${contentType}|${btoa(bin)}`, { expirationTtl: CACHE_TTL });
|
||||
}
|
||||
return new Response(buf, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
return new Response(buf, { status: 200, headers: imageCacheHeaders(contentType) });
|
||||
}
|
||||
} catch {
|
||||
/* 落到第三方兜底 */
|
||||
@@ -182,14 +190,7 @@ export async function favicon(request: Request, env: Env): Promise<Response> {
|
||||
bin += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + chunk)) as unknown as number[]);
|
||||
}
|
||||
await env.RSS_KV.put(bodyKey, `${contentType}|${btoa(bin)}`, { expirationTtl: CACHE_TTL });
|
||||
return new Response(buf, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
return new Response(buf, { status: 200, headers: imageCacheHeaders(contentType) });
|
||||
}
|
||||
} catch {
|
||||
/* 彻底失败 */
|
||||
@@ -212,8 +213,11 @@ function letterSvg(siteUrl: string): Response {
|
||||
return new Response(svg, {
|
||||
status: 200,
|
||||
headers: {
|
||||
// 兜底图也缓存(否则每个访客都要重跑一遍抓取失败流程 = 3 次 KV 读 + 1 次写)
|
||||
'Content-Type': 'image/svg+xml',
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
'Cache-Control': 'public, max-age=86400, s-maxage=86400',
|
||||
'CDN-Cache-Control': 'max-age=86400',
|
||||
'Cloudflare-CDN-Cache-Control': 'max-age=86400',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user