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