head 只有一个来源:SiteHead、JSON-LD 转义与 hreflang
三个 HTML 外壳共用一个组件输出全部 head 标签,数据在 server 层组装。以及一个必须记住的细节:JSON-LD 里的尖括号要转义成反斜杠 u003c。
一个站点最容易长歪的地方是 head。首页加一个 og:image,文章页加一段 JSON-LD,404 页抄一半——三个月后没人说得清哪个页面到底有哪些标签。
唯一来源
components/SiteHead.astro 输出 head 里的全部标签,数据由 server/shell.ts 的 shellHead() 组装。三个 HTML 外壳(两个 layout 与语言入口页)都渲染它。要改 SEO,只改这两个文件。
JSON-LD 里的转义
结构化数据是内联 JSON,序列化后必须把 < 换成 \u003c:
const json = JSON.stringify(data).replace(/</g, '\\u003c');
不转义的话,文章标题里出现 </script> 就会提前闭合标签,后面的 HTML 全被当成脚本。这不是理论问题:标题是作者可写的输入。
hreflang 与 canonical
每页输出三条 hreflang 加一条 x-default,x-default 固定指向中文。canonical 只认正式域名(带 www)——裸域名在 Nginx 那层 301 过来,两个 host 都开站点会变成重复内容。
robots.txt 也是构建期产物
它由 pages/robots.txt.ts 生成,引用 lib/site.ts 的 FALLBACK_ORIGIN,不放在 public/。理由很实际:静态文件在换域名时会被忘掉,构建期生成不会。
为什么值得这么严
head 的错误没有视觉反馈。og:image 配错了,页面看起来完全正常,只有分享卡片是坏的;canonical 配错了,只有搜索引擎知道。把唯一性当成硬约束,是这类问题唯一能被静态检查的方式。

评论
…