[{"data":1,"prerenderedAt":445},["ShallowReactive",2],{"content:\u002F2026\u002Fcf-cache-rules":3,"surround:\u002F2026\u002Fcf-cache-rules":434},{"id":4,"title":5,"body":6,"categories":407,"date":409,"description":410,"draft":411,"extension":412,"image":413,"meta":414,"navigation":416,"path":417,"permalink":418,"published":418,"readingTime":419,"recommend":418,"references":418,"seo":424,"sitemap":425,"stem":426,"tags":427,"type":432,"updated":409,"__hash__":433},"content\u002Fposts\u002F2026\u002Fcf-cache-rules.md","域名走 Cloudflare 优选后，怎么配缓存规则",{"type":7,"value":8,"toc":395},"minimark",[9,13,17,20,31,34,59,75,86,89,92,95,102,108,115,121,125,132,135,142,160,168,174,177,191,197,204,207,210,216,219,241,244,247,253,256,259,262,267,270,273,276,279,282,288,306,323,326,331,334,340,353,356,362,365,368,371,392],[10,11,12],"h2",{"id":12},"我开始的想法",[14,15,16],"p",{},"我的博客是 Nuxt 生成的静态站，部署在 Cloudflare Pages。为了让国内访问好一点，域名又套了一层 Cloudflare 优选 CNAME。",[14,18,19],{},"最后的链路大概是这样：",[21,22,28],"pre",{"className":23,"code":25,"language":26,"meta":27},[24],"language-txt","访客\n-> blog.zery.cn\n-> 优选 CNAME\n-> Cloudflare Pages\n-> 静态文件\n","txt","",[29,30,25],"code",{"__ignoreMap":27},[14,32,33],{},"然后我发现个问题:",[35,36,38,44],"alert",{"type":37},"warning",[39,40,41],"template",{"v-slot:title":27},[14,42,43],{},"优选只是在选好线路，该加载的资源不会变。",[14,45,46,47,50,51,54,55,58],{},"最终访问 ",[29,48,49],{"code":49},"blog.zery.cn"," 时，除了路线，网站本应加载的资源对速度也有一定影响，特别是大头文件，正确规划他们的缓存规则就很重要了， ",[29,52,53],{"code":53},"边缘缓存","、",[29,56,57],{"code":57},"浏览器缓存","...",[14,60,61,62,65,66,70,71,74],{},"我一开始想偷懒：全站直接 ",[29,63,64],{"code":64},"\u002F*"," 缓一年，多省事。后来发现 Nuxt 静态站",[67,68,69],"del",{},"大部分博客都是","不能这么粗暴，尤其是 ",[29,72,73],{"code":73},"_nuxt"," 下面那些带 hash 的资源和普通 HTML 不是一类东西。",[76,77,78,81],"quote",{},[14,79,80],{},"当时的心理活动：都静态博客了，给我缓存一年怎么了？",[39,82,83],{"v-slot:icon":27},[14,84,85],{},"缓存",[14,87,88],{},"不过话说回来，缓存对速度的帮助确实很明显，不是那种“心理优化”。最直观的例子就是刷新当前页面：有缓存时，浏览器能直接用本地缓存，或者很快从离自己近的 CDN 节点拿到结果，体感就是几乎秒开；没有缓存时，就要重新走一遍请求链路，HTML、payload、CSS、JS 都得重新拉，慢的时候差距非常明显。",[10,90,91],{"id":91},"三类缓存文件",[14,93,94],{},"Nuxt 静态博客生成出来后，常见资源大概分三类。",[14,96,97,98,101],{},"第一类是页面和数据，比如首页、文章页、归档页、",[29,99,100],{"code":100},"_payload.json","。这些内容会跟着发文章变化，适合短缓存。",[14,103,104,105,107],{},"第二类是 ",[29,106,73],{"code":73}," 里的 JS\u002FCSS。它们通常文件名带 hash，内容变了文件名也会变。这类资源适合长缓存，Cloudflare Pages 默认就会给比较激进的缓存头1年。",[14,109,110,111,114],{},"第三类是预览页、后台接口一类的路径。如果内容可能随时变，或者不想被中间层存住，就应该加 ",[29,112,113],{"code":113},"no-store","。",[21,116,119],{"className":117,"code":118,"language":26,"meta":27},[24],"HTML \u002F payload       短缓存\n\u002F_nuxt\u002F*             让 Nuxt \u002F Pages 默认处理\n\u002Fpreview*            不缓存\n字体、图片转换结果    长一点缓存\n",[29,120,118],{"__ignoreMap":27},[10,122,124],{"id":123},"最好不要直接写全站","最好不要直接写全站 \u002F*",[14,126,127,128,131],{},"我踩过的坑是：Cloudflare Pages 的 ",[29,129,130],{"code":130},"_headers"," 不是“最后命中一条就结束”。多个规则同时匹配时，响应头可能会合并。",[14,133,134],{},"如果你写：",[21,136,140],{"className":137,"code":138,"filename":139,"language":26,"meta":27},[24],"\u002F*\n  Cache-Control: public, max-age=300, s-maxage=1800\n\n\u002F_nuxt\u002F*\n  Cache-Control: public, max-age=31536000, immutable\n","public\u002F_headers",[29,141,138],{"__ignoreMap":27},[14,143,144,145,148,149,151,152,155,156,159],{},"访问 ",[29,146,147],{"code":147},"\u002F_nuxt\u002Fentry.xxx.css"," 时，它既命中 ",[29,150,64],{"code":64},"，也命中 ",[29,153,154],{"code":154},"\u002F_nuxt\u002F*","。结果就可能变成两个 ",[29,157,158],{"code":158},"Cache-Control"," 混在一起：",[21,161,166],{"className":162,"code":164,"language":165,"meta":27},[163],"language-http","Cache-Control: public, max-age=300, s-maxage=1800, public, max-age=31536000, immutable\n","http",[29,167,164],{"__ignoreMap":27},[14,169,170,171,173],{},"这就很尴尬了。你本来想让 ",[29,172,73],{"code":73}," 走一年强缓存，结果短缓存也混进来了。到底哪个生效、不同中间层怎么理解，都不如一开始就别让它混在一起。",[10,175,176],{"id":176},"我最后用的规则",[14,178,179,180,183,184,187,188,190],{},"我的文章路径是 ",[29,181,182],{"code":182},"\u002F2026\u002Fhello-world"," 这种年份开头，所以用 ",[29,185,186],{"code":186},"\u002F20*"," 来覆盖文章页和文章 payload。它不会碰到 ",[29,189,154],{"code":154},"，维护起来也比每篇文章手写路径舒服。",[21,192,195],{"className":193,"code":194,"filename":139,"language":26,"meta":27},[24],"\u002F\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002F:page\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002F:page\u002F\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002F:page\u002F_payload.json\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002F20*\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002Fapi\u002F*\n  ! Cache-Control\n  Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\n\u002Ffonts\u002F*\n  ! Cache-Control\n  Cache-Control: public, max-age=31536000, immutable\n\n\u002F_ipx\u002F*\n  ! Cache-Control\n  Cache-Control: public, max-age=2592000, stale-while-revalidate=86400\n\n\u002Fassets\u002F*\n  ! Cache-Control\n  Cache-Control: public, max-age=604800, stale-while-revalidate=86400\n\n\u002Fpreview\n  ! Cache-Control\n  Cache-Control: no-store\n\n\u002Fpreview\u002F\n  ! Cache-Control\n  Cache-Control: no-store\n\n\u002Fpreview\u002F*\n  ! Cache-Control\n  Cache-Control: no-store\n",[29,196,194],{"__ignoreMap":27},[14,198,199,200,203],{},"这里的 ",[29,201,202],{"code":202},"! Cache-Control"," 是为了先移除已有的同名响应头，再写自己的值。这样可以减少重复 header 的合并。",[10,205,206],{"id":206},"缓存头的每个值",[14,208,209],{},"短缓存这一行：",[21,211,214],{"className":212,"code":213,"language":165,"meta":27},[163],"Cache-Control: public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n",[29,215,213],{"__ignoreMap":27},[14,217,218],{},"我的理解是：",[220,221,222,229,235],"ul",{},[223,224,225,228],"li",{},[29,226,227],{"code":227},"max-age=300","：浏览器本地缓存 5 分钟，文章改了不至于在读者电脑里卡太久。",[223,230,231,234],{},[29,232,233],{"code":233},"s-maxage=1800","：共享缓存，比如 CDN，缓存 30 分钟。",[223,236,237,240],{},[29,238,239],{"code":239},"stale-while-revalidate=86400","：缓存过期后，可以先返回旧内容，同时后台去更新。",[14,242,243],{},"这套配置比较适合静态博客：平时访问能吃到 CDN 缓存，发文章后如果很急，可以手动清一下缓存；不急的话，等一会儿也会慢慢刷新。",[14,245,246],{},"长缓存这一行：",[21,248,251],{"className":249,"code":250,"language":165,"meta":27},[163],"Cache-Control: public, max-age=31536000, immutable\n",[29,252,250],{"__ignoreMap":27},[14,254,255],{},"只适合文件名会变的资源，比如字体、带 hash 的静态资源。别给 HTML 这么写，不然你改了文章，读者浏览器可能还在看很久以前的页面。",[10,257,258],{"id":258},"自己测一下",[14,260,261],{},"不要只看后台规则有没有保存。最终以线上域名返回的响应头为准。",[263,264],"copy",{"code":265,"prompt":266},"curl.exe -I https:\u002F\u002Fblog.zery.cn\u002F","PS>",[263,268],{"code":269,"prompt":266},"curl.exe -I https:\u002F\u002Fblog.zery.cn\u002F2026\u002Fhello-world",[263,271],{"code":272,"prompt":266},"curl.exe -I https:\u002F\u002Fblog.zery.cn\u002Farchive\u002F_payload.json",[263,274],{"code":275,"prompt":266},"curl.exe -I https:\u002F\u002Fblog.zery.cn\u002F_nuxt\u002Fentry.CgjSPprJ.css",[263,277],{"code":278,"prompt":266},"curl.exe -I https:\u002F\u002Fblog.zery.cn\u002Fpreview",[14,280,281],{},"正常情况下，我希望看到类似这样：",[21,283,286],{"className":284,"code":285,"language":26,"meta":27},[24],"\u002F                         public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\u002F2026\u002Fhello-world          public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\u002Farchive\u002F_payload.json     public, max-age=300, s-maxage=1800, stale-while-revalidate=86400\n\u002F_nuxt\u002Fentry.xxx.css       public, max-age=31536000, immutable\n\u002Fpreview                   no-store\n",[29,287,285],{"__ignoreMap":27},[14,289,290,291,293,294,296,297,54,299,302,303,305],{},"如果 ",[29,292,73],{"code":73}," 的 CSS \u002F JS 也变成了 ",[29,295,227],{"code":227},"，基本就是规则写宽了。回去检查有没有 ",[29,298,64],{"code":64},[29,300,301],{"code":301},"\u002F:year\u002F:slug"," 这种把 ",[29,304,73],{"code":73}," 一起扫进去的规则。",[14,307,308,311,312,315,316,54,319,322],{},[29,309,310],{"code":310},"CF-Cache-Status"," 和 ",[29,313,314],{"code":314},"Age"," 可以顺手看，但别太相信第一次结果。刚部署完、刚清缓存、或者某个边缘节点第一次访问，",[29,317,318],{"code":318},"MISS",[29,320,321],{"code":321},"Age: 0"," 都很正常。多请求几次，或者换一个页面再看，才比较接近真实状态。",[10,324,325],{"id":325},"在哪里配置",[14,327,328,329,114],{},"如果是 Cloudflare Pages 静态站，我更愿意把这些规则放在仓库的 ",[29,330,139],{"code":139},[14,332,333],{},"原因很简单：它跟着代码走。换机器、重新部署，都不会忘记自己当时到底在后台点了什么。",[14,335,336,337,339],{},"我也试过直接在 Cloudflare 控制台的 Cache Rules 里配，但这条链路下不稳定，至少我这里没生效。原因是 ",[29,338,49],{"code":49}," 前面走了优选 CNAME，DNS 记录本身不是普通小黄云代理入口；Cache Rules 要在“这次请求真正经过的那个 Cloudflare 代理层”执行，才会改到最终响应。",[14,341,342,343,346,347,349,350,352],{},"所以我最后的判断标准很简单：后台缓存规则保存成功了不算，",[29,344,345],{"code":345},"curl -I https:\u002F\u002Fblog.zery.cn\u002F..."," 看到 ",[29,348,158],{"code":158}," 真的变了才算数。既然我控制台规则试下来没改动响应头，就把缓存规则写到 Pages 输出的 ",[29,351,130],{"code":130}," 里，让cf能get到我的缓存规则。",[14,354,355],{},"当然，如果你前面还有一层别的 CDN 或优选服务，它也可能有自己的缓存规则。我的习惯是先让看看源站响应头怎么缓存，看前面的 CDN 对它有没有影响。",[357,358],"link-card",{"icon":359,"link":360,"title":361},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Ffavicon.png","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fpages\u002Fconfiguration\u002Fheaders\u002F","Cloudflare Pages：Headers",[10,363,364],{"id":364},"更新文章后怎么办",[14,366,367],{},"这套规则不是“永远不刷新”，而是把刷新节奏压到一个能接受的范围。",[14,369,370],{},"平时发文章，我一般这样处理：",[372,373,374,377,386,389],"ol",{},[223,375,376],{},"提交文章，等 Cloudflare Pages 重新部署。",[223,378,379,380,383,384,114],{},"线上用 ",[29,381,382],{"code":382},"curl -I"," 看首页、文章页、payload 的 ",[29,385,158],{"code":158},[223,387,388],{},"如果只是普通更新，等短缓存自然过期。",[223,390,391],{},"如果是错别字、链接错误、重要内容，直接去 CDN 后台清缓存。",[14,393,394],{},"优选域名只是让访问路线更顺一点，缓存规则才是让第二次、第三次访问轻一点。两者不是互相替代的关系，各行其责，让网站访问体验更好。",{"title":27,"searchDepth":396,"depth":396,"links":397},4,[398,400,401,402,403,404,405,406],{"id":12,"depth":399,"text":12},2,{"id":91,"depth":399,"text":91},{"id":123,"depth":399,"text":124},{"id":176,"depth":399,"text":176},{"id":206,"depth":399,"text":206},{"id":258,"depth":399,"text":258},{"id":325,"depth":399,"text":325},{"id":364,"depth":399,"text":364},[408],"部署运维","2026-05-16 12:56:25","记录一次 Cloudflare Pages 静态博客在优选 CNAME 后的缓存规则配置。",false,"md","https:\u002F\u002Fimg.zery.cn\u002F202605\u002Fcf-cache-rules.webp",{"slots":415},{},true,"\u002F2026\u002Fcf-cache-rules",null,{"text":420,"minutes":421,"time":422,"words":423},"9 min read",8.335,500100,1667,{"title":5,"description":410},{"loc":417},"posts\u002F2026\u002Fcf-cache-rules",[428,429,430,85,431],"Cloudflare","Cloudflare Pages","CDN","Nuxt","tech","yM58uwvReFFgindxqx0VxH_-YWdjg-9LVYe1eTfowps",[435,440],{"title":436,"path":437,"stem":438,"date":439,"type":432,"children":-1},"Twikoo 评论 IP 属地不对，把后端改成 IPv4-only","\u002F2026\u002Ftwikoo-ipv4","posts\u002F2026\u002Ftwikoo-ipv4","2026-05-12 14:35:00",{"title":441,"path":442,"stem":443,"date":444,"type":432,"children":-1},"今天把 Open WebUI 的文件残留清干净了","\u002F2026\u002Fopenwebui-cleanup-journey","posts\u002F2026\u002Fopenwebui-cleanup-journey","2026-06-14 17:55:30",1781452628132]