[{"data":1,"prerenderedAt":386},["ShallowReactive",2],{"blog-cache-control-misconfig":3,"blog-prev-cache-control-misconfig":204,"blog-next-cache-control-misconfig":291},{"id":4,"title":5,"author":6,"body":7,"canonical":189,"category":190,"cover":189,"date":191,"description":192,"draft":193,"extension":194,"keywords":189,"meta":195,"navigation":196,"noindex":193,"ogImage":189,"path":197,"seo":198,"seoDescription":189,"seoTitle":189,"stem":199,"tags":200,"updated":189,"__hash__":203},"blog\u002Fblog\u002Fcache-control-misconfig.md","静态资源缓存头配错的四种样子，我们逐个拆开","元析智能",{"type":8,"value":9,"toc":179},"minimark",[10,14,19,34,37,41,44,47,51,61,68,72,75,78,82,85,88,91,94,136,142,145,148,172,175],[11,12,13],"p",{},"缓存头配错，表现不是页面打不开，而是\"改了没生效\"和\"每次都重新下载\"。今年 1 月我们给一个软件站做资源审计，四种错误全占了，最离谱的是 3.4MB 的静态资源每周产生 12 万次 304 请求。",[15,16,18],"h2",{"id":17},"错误一html-和带-hash-的资源用同一套头","错误一：HTML 和带 hash 的资源用同一套头",[11,20,21,25,26,29,30,33],{},[22,23,24],"code",{},"max-age=0, must-revalidate"," 配在 HTML 上是对的，配在 ",[22,27,28],{},"main.a1b2c3.js"," 上就是纯浪费。带 hash 的文件内容一变名字就变，可以直接 ",[22,31,32],{},"max-age=31536000, immutable","。",[11,35,36],{},"这个站两种资源共用一套配置，结果每次访问都要对 40 多个静态文件做条件请求，移动端多花 300 毫秒。",[15,38,40],{"id":39},"错误二改了文件没改-hash","错误二：改了文件没改 hash",[11,42,43],{},"反向的坑。文件名没变、内容变了、头又是长缓存，用户三个月看不到新版本。我们见过一个站改了 CSS 但没改 hash，客服被投诉两周\"按钮错位\"。",[11,45,46],{},"查法：对比 CDN 上的文件名和构建产物里的 hash。改法是把构建的 hash 和内容绑定做好，别用时间戳糊弄，时间戳一秒内构建两次就会撞。",[15,48,50],{"id":49},"错误三cdn-上用了-private-或-no-store","错误三：CDN 上用了 private 或 no-store",[11,52,53,54,57,58,60],{},"有些框架默认给所有响应加 ",[22,55,56],{},"no-store","，CDN 一看就不缓存，等于白买。这个站的营销页 HTML 就是 ",[22,59,56],{},"，回源率 100%，TTFB 高得离谱。",[11,62,63,64,67],{},"改成 ",[22,65,66],{},"public, max-age=600"," 之后，回源请求从每天 6 万降到 8 千。这一步没改任何代码，只改了响应头。",[15,69,71],{"id":70},"错误四多台机器各算-etag","错误四：多台机器各算 ETag",[11,73,74],{},"ETag 默认基于文件 inode 和修改时间生成。多台机器部署同一个文件，算出来的 ETag 各不相同，用户在不同节点间切换就会反复全量下载。",[11,76,77],{},"改法是在构建时统一生成基于内容哈希的 ETag，或者干脆去掉 ETag，靠 Cache-Control 和文件名版本控制。",[15,79,81],{"id":80},"一个附带问题service-worker-和缓存头打架","一个附带问题：Service Worker 和缓存头打架",[11,83,84],{},"有些软件站为了做离线可用或者推送，注册了 Service Worker。它有自己的缓存层，会绕过浏览器的 HTTP 缓存策略。这时候你把 Cache-Control 改对了，用户还是看到旧版本，因为 SW 把旧资源扣在手里。",[11,86,87],{},"查法是 DevTools 的 Application 面板里看 Service Workers 和 Cache Storage，改完记得版本号加一。我们遇到过一个站，SW 缓存了三个月前的 JS，排查了两天才想到这一层。",[15,89,90],{"id":90},"怎么验证改对了",[11,92,93],{},"改完头别只看代码，用 curl 直接看线上响应：",[95,96,101],"pre",{"className":97,"code":98,"language":99,"meta":100,"style":100},"language-bash shiki shiki-themes github-light github-dark","curl -sI https:\u002F\u002Fexample.com\u002Fmain.a1b2c3.js | grep -i -E \"cache-control|etag|age\"\n","bash","",[22,102,103],{"__ignoreMap":100},[104,105,108,112,116,120,124,127,130,133],"span",{"class":106,"line":107},"line",1,[104,109,111],{"class":110},"sScJk","curl",[104,113,115],{"class":114},"sj4cs"," -sI",[104,117,119],{"class":118},"sZZnC"," https:\u002F\u002Fexample.com\u002Fmain.a1b2c3.js",[104,121,123],{"class":122},"szBVR"," |",[104,125,126],{"class":110}," grep",[104,128,129],{"class":114}," -i",[104,131,132],{"class":114}," -E",[104,134,135],{"class":118}," \"cache-control|etag|age\"\n",[11,137,138,141],{},[22,139,140],{},"age"," 这个头很有用，它是 CDN 缓存的时间戳，能直接看出命中了没有。命中时 age 会递增，回源时是 0。",[15,143,144],{"id":144},"十分钟检查清单",[11,146,147],{},"我们给客户的顺序是三件事：",[149,150,151,162,169],"ol",{},[152,153,154,155,158,159],"li",{},"打开首页，看 HTML 的 ",[22,156,157],{},"Cache-Control"," 是不是 ",[22,160,161],{},"public",[152,163,164,165,168],{},"看带 hash 的 JS 和 CSS 是不是 ",[22,166,167],{},"immutable"," 长缓存",[152,170,171],{},"去 CDN 后台看缓存命中率，低于 80% 就有问题",[11,173,174],{},"三项查完十分钟，比读代码见效快。缓存这类问题有个特点：配置错误一次，之后每个用户都在承担代价，而且没人投诉，因为大家以为\"网站就这样\"。",[176,177,178],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":100,"searchDepth":180,"depth":180,"links":181},2,[182,183,184,185,186,187,188],{"id":17,"depth":180,"text":18},{"id":39,"depth":180,"text":40},{"id":49,"depth":180,"text":50},{"id":70,"depth":180,"text":71},{"id":80,"depth":180,"text":81},{"id":90,"depth":180,"text":90},{"id":144,"depth":180,"text":144},null,"技术SEO","2026-01-01","一个软件站的资源审计，四种缓存头错误全占了：HTML 和 hash 资源共用一套头、改文件没改 hash、CDN 上用 no-store、多机 ETag 不一致。附十分钟能跑完的检查清单。",false,"md",{},true,"\u002Fblog\u002Fcache-control-misconfig",{"title":5,"description":192},"blog\u002Fcache-control-misconfig",[157,201,202],"HTTP缓存","静态资源","bxf_OiUT5ysR8ewmkR365YQa8SrFBzLHzRBxnsnjmPk",{"id":205,"title":206,"author":6,"body":207,"canonical":189,"category":190,"cover":189,"date":280,"description":281,"draft":193,"extension":194,"keywords":189,"meta":282,"navigation":196,"noindex":193,"ogImage":189,"path":283,"seo":284,"seoDescription":189,"seoTitle":189,"stem":285,"tags":286,"updated":189,"__hash__":290},"blog\u002Fblog\u002Fjson-ld-article-faq-fields.md","文章和问答的结构化数据，五个字段最容易写错",{"type":8,"value":208,"toc":271},[209,212,215,218,222,229,232,235,239,242,246,253,256,259,262,265,268],[11,210,211],{},"结构化数据报错，九成不是 JSON 语法错，是标记的内容和页面可见内容对不上。校验器能过，不等于搜索引擎认。下面五个字段是我们改客户站时最常返工的。",[15,213,214],{"id":214},"headline",[11,216,217],{},"必须和页面 H1 一致，长度控制在 110 个字符以内。常见错法是把 title 标签整串塞进去，带上\" | 品牌名\"后缀，或者为了关键词硬改 H1。headline 和 H1 不一致，Google 会以页面为准，标记被判无效。",[15,219,221],{"id":220},"datepublished-和-datemodified","datePublished 和 dateModified",[11,223,224,225,228],{},"格式必须是带时区的 ISO 8601，比如 ",[22,226,227],{},"2026-01-15T09:00:00+08:00","。只写日期也能过校验，但改造过的旧文章如果 dateModified 早于 datePublished，整条标记作废。这两项要和页面上显示的发布时间对得上，别为了\"显得新\"改日期。",[15,230,231],{"id":231},"author",[11,233,234],{},"文章署名是公司就用 Organization，个人就用 Person，name 必须和页面上署的名字一字不差。我们见过 320 篇文章的 author 全写着 \"Admin\"，GSC 报告里全报作者无效。",[15,236,238],{"id":237},"faq-主体","FAQ 主体",[11,240,241],{},"Question 的 name 是问题原文，acceptedAnswer 的 text 是答案正文，必须和页面可见的问答内容一致。答案里如果要保留加粗，用受支持的标签，别塞自定义组件。还有一条容易忽略：页面上要有真实的问答区块，不能只在 JSON-LD 里写问答，页面正文根本没有——这属于作弊，会被判垃圾结构化数据。",[15,243,245],{"id":244},"inlanguage","inLanguage",[11,247,248,249,252],{},"和页面语言一致。多语言站很容易漏，中文页写了 ",[22,250,251],{},"en","，整条标记作废。",[15,254,255],{"id":255},"怎么查才快",[11,257,258],{},"三件事就够了。第一，Rich Results Test 跑单页，直接告诉你哪条字段无效，但它不覆盖全部类型。第二，GSC 的\"增强功能\"报告按类型汇总全站错误，一次能看几百条，适合批量修。第三，把页面源码里的 JSON-LD 抠出来过一遍 JSON 校验，语法错通常死在这里，比如尾逗号、双引号没转义、时间字段写成中文日期。",[11,260,261],{},"我们的做法是把 JSON-LD 做成模板函数，入参是标题、作者、发布时间、封面图，前端统一调用，模板一改全站生效。手写 JSON-LD 的站，改完 300 篇能漏 80 篇。",[15,263,264],{"id":264},"一个判断",[11,266,267],{},"FAQ 的富媒体展示从 2023 年 8 月起就大幅收缩了，普通商业站基本看不到问答展开样式。所以别为了标记硬造问答，页面上本来没有 FAQ 就别加。Article 标记相反，成本低、一直有效，每篇文章都该有。",[11,269,270],{},"还有 Organization 和 BreadcrumbList，前者放在全站模板，后者跟着导航走。四类标记，一套模板解决，不用一篇篇手写。",{"title":100,"searchDepth":180,"depth":180,"links":272},[273,274,275,276,277,278,279],{"id":214,"depth":180,"text":214},{"id":220,"depth":180,"text":221},{"id":231,"depth":180,"text":231},{"id":237,"depth":180,"text":238},{"id":244,"depth":180,"text":245},{"id":255,"depth":180,"text":255},{"id":264,"depth":180,"text":264},"2025-12-31","FAQ 富媒体展示早就收紧了，Article 标记却一直值得做。我们按真实报错整理出五个最常写错的字段：headline、日期、author、FAQ 主体、语言，每个给正确和错误写法。",{},"\u002Fblog\u002Fjson-ld-article-faq-fields",{"title":206,"description":281},"blog\u002Fjson-ld-article-faq-fields",[287,288,289],"结构化数据","JSON-LD","Article","MaikUshocMuPFKqdfdiF3sOundW55OcmZuYC0xfWxZE",{"id":292,"title":293,"author":6,"body":294,"canonical":189,"category":374,"cover":189,"date":375,"description":376,"draft":193,"extension":194,"keywords":189,"meta":377,"navigation":196,"noindex":193,"ogImage":189,"path":378,"seo":379,"seoDescription":189,"seoTitle":189,"stem":380,"tags":381,"updated":189,"__hash__":385},"blog\u002Fblog\u002Fquestion-words-support-tickets.md","问题词从客服工单里挖，比关键词工具靠谱",{"type":8,"value":295,"toc":365},[296,299,302,305,308,311,314,317,320,323,326,329,332,335,338,341,344,347,350,353,356,359,362],[11,297,298],{},"最准的问题词表不在关键词工具里，在客服工单里。用户遇到问题时会同时做两件事：找客服，和去搜索引擎里打一句话。这两句话几乎是一样的。",[15,300,301],{"id":301},"工单里能挖出三类词",[11,303,304],{},"第一类是报错原文，比如“连接被拒绝”“证书过期”。第二类是功能叫法，用户管它叫“同步”，你们内部叫“数据集成”，搜索量差十倍。第三类是操作步骤，比如“怎么批量导入”“怎么撤销删除”。",[11,306,307],{},"这三类词工具一般挖不出来，因为量太小，被工具当噪音过滤了。",[15,309,310],{"id":310},"我们做的一次抽取",[11,312,313],{},"2025 年 11 月，我们让一个客户导出近 6 个月共 1200 条工单标题，按动词聚成 8 组：配置、导入、导出、报错、权限、计费、升级、对接。",[11,315,316],{},"人工过一遍抽出 87 个短语，再去搜索后台对照。其中 31 个在后台有稳定曝光，但工具显示月搜不到 50。",[15,318,319],{"id":319},"客户用词和你们用词不是一回事",[11,321,322],{},"这一步最容易漏。客户说“证书”，你们的文档写“TLS 凭证”；客户说“多账号”，文档写“多租户”。搜客户用词的人，永远找不到你们的文档。",[11,324,325],{},"我们的做法是：正文里两个词都出现一次，标题用客户的说法，文档内部继续用规范说法。",[15,327,328],{"id":328},"这些词该写成什么页面",[11,330,331],{},"问题词大多该写文档页、排错页、FAQ，而不是博客文章。博客是给还在了解的人看的，问题词是给已经在用的人看的。",[11,333,334],{},"一个排错页做得好，能带来续费和口碑，虽然它看起来不像内容营销。",[15,336,337],{"id":337},"每月半小时的流程",[11,339,340],{},"客服每月导一次工单标题，我们花 30 分钟过一遍，新的进长尾池，重复的并进已有页面的问题列表。",[11,342,343],{},"这个动作我们从 2025 年 8 月起固定下来，四个月里给客户的文档页补了 60 多个问题句。",[15,345,346],{"id":346},"挖出来的词别只做内容",[11,348,349],{},"工单里的问题词，一半以上在提示产品本身的缺口。一个问题被问 50 次，说明界面或文档没讲清楚，这时候该改产品文案，不是只写文章。",[11,351,352],{},"我们 2025 年 10 月把一批高频问题反馈给客户的产品团队，他们改了三个提示文案，同类工单一个月下降了 30%。内容和技术在这里是同一条链。",[15,354,355],{"id":355},"一个别做的事",[11,357,358],{},"不要把工单原文直接贴到页面上。用户的问题带着账号、订单、内部名称，脱敏要做干净，能改写成通用问句就改。",[11,360,361],{},"我们见过一个客户把工单标题原样做成 FAQ，里面带着客户公司名，后来被对方发现，关系闹得很僵。",[11,363,364],{},"工单是最便宜的关键词研究。不花钱，不依赖工具，还自带真实语境。",{"title":100,"searchDepth":180,"depth":180,"links":366},[367,368,369,370,371,372,373],{"id":301,"depth":180,"text":301},{"id":310,"depth":180,"text":310},{"id":319,"depth":180,"text":319},{"id":328,"depth":180,"text":328},{"id":337,"depth":180,"text":337},{"id":346,"depth":180,"text":346},{"id":355,"depth":180,"text":355},"SEO","2026-01-02","最准的词表不在工具里，在工单里。2025 年 11 月我们导了 1200 条工单，抽出 87 个真实搜索词，其中 31 个月搜不到 50，但搜索后台有稳定曝光。",{},"\u002Fblog\u002Fquestion-words-support-tickets",{"title":293,"description":376},"blog\u002Fquestion-words-support-tickets",[382,383,384],"问题词","关键词研究","客服工单","-XLIeTJrYpSKClW7h6O3rJ4fEAnuBdHnb7_h1EqqDK0",1794088109567]