<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>酥酥鸡</title><description>一边写代码，一边记录生活</description><link>https://susuji.top/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.4</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年10月25日 20:16:36</lastBuildDate><item><title>你好，这里是酥酥鸡的博客</title><link>https://susuji.top/posts/hello-susuji/</link><guid isPermaLink="true">https://susuji.top/posts/hello-susuji/</guid><description>从今天开始，在自己的小站里认真记录前端技术、建站实践与平凡生活。</description><pubDate>Wed, 26 Aug 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;你好，欢迎来到酥酥鸡的博客。&lt;/p&gt;
&lt;p&gt;这是这个小站正式发布后的第一篇文章。比起把它当成一份精心包装的个人简历，我更愿意把它看作一张长期使用的书桌：一边放正在学习的技术，一边放生活里值得记住的小事。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;为什么重新拥有一个博客&lt;a href=&quot;#为什么重新拥有一个博客&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;平时接触到的知识很零散。一个报错的解决办法、一个组件的设计取舍，或者一次部署踩过的坑，如果只停留在聊天记录和临时笔记里，很快就会失去上下文。&lt;/p&gt;&lt;p&gt;博客给了这些内容一个更稳定的位置。写作也迫使我重新检查自己的理解：哪些只是“碰巧跑通”，哪些才是真正可以复用的方法。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Note&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;这里的文章会尽量区分事实、判断和个人偏好。能够验证的内容会给出过程，仍有不确定性的地方也会明确说明。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;这里会写什么&lt;a href=&quot;#这里会写什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;目前计划记录三类内容：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;前端开发：Astro、组件设计、样式系统、性能和可访问性；&lt;/li&gt;
&lt;li&gt;建站实践：静态博客、搜索、Nginx、HTTPS、发布与回滚；&lt;/li&gt;
&lt;li&gt;生活随笔：那些不适合写成教程，却值得留下来的片段。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;技术文章不会为了显得完整而虚构环境，生活内容也不会为了更新频率而勉强拼凑。比起追求数量，我更希望每一篇内容在一段时间后仍然对自己或读者有用。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;关于这个小站&lt;a href=&quot;#关于这个小站&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;本站使用 &lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot;&gt;Astro&lt;/a&gt; 构建，界面基于 &lt;a href=&quot;https://github.com/CuteLeaf/Firefly&quot; target=&quot;_blank&quot;&gt;Firefly&lt;/a&gt; 主题定制。它最终会生成纯静态文件，由 Nginx 提供访问；站内搜索由 Pagefind 在构建阶段生成索引。&lt;/p&gt;&lt;p&gt;我保留了 Firefly 的开源署名，同时调整了配色、布局、字体和侧边栏，让它更像属于自己的空间。关于这部分的具体过程，会在后面的文章里慢慢展开。&lt;/p&gt;&lt;p&gt;如果你愿意持续关注，可以使用本站的 &lt;a href=&quot;/rss/&quot;&gt;RSS&lt;/a&gt; 订阅。现在内容还不多，但这张书桌已经摆好了。&lt;/p&gt;&lt;p&gt;感谢来访，我们下一篇见。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>给博客做一次性能体检：Lighthouse 56 分的复盘与清单</title><link>https://susuji.top/posts/lighthouse-first-checkup/</link><guid isPermaLink="true">https://susuji.top/posts/lighthouse-first-checkup/</guid><description>一次真实的性能基线记录：性能 56 分不意味着失败，它只是把「功能堆满了」这件事变成了具体的数字。</description><pubDate>Tue, 08 Sep 2026 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;九月初，我给这个刚上线一周的博客做了一次完整的 Lighthouse 体检。结果如下：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;指标&lt;/th&gt;&lt;th&gt;得分&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;性能&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;可访问性&lt;/td&gt;&lt;td&gt;96&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;最佳实践&lt;/td&gt;&lt;td&gt;81&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SEO&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;同一时间点的体积数据：首页 HTML 309 KB，完整构建产物 10.8 MB。&lt;/p&gt;
&lt;p&gt;朋友圈式的做法是只贴四项里最高的那个。但这份体检真正的信息量，恰恰在那个最难看的 56 分里。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;「静态网站 = 快」的误解&lt;a href=&quot;#静态网站--快的误解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;动手之前，我默认静态站一定快：没有服务端渲染，没有数据库查询，文件在 Nginx 上即取即回。这个判断只对了一半。静态意味着服务端开销趋近于零，但用户感知的速度取决于首屏要下载多少东西——HTML、CSS、JavaScript、字体、图片，一个都不会少。&lt;/p&gt;&lt;p&gt;翻开首页的构成就很清楚：309 KB 的 HTML 说明首页是「一整页」输出；外观设置面板、音乐播放器、灯箱、樱花与水波纹特效、模拟时钟……这些功能各自带着脚本和样式，而且大多在页面加载时就被立即初始化。功能堆满了，浏览器首屏的负担也就堆满了。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Note&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;Astro 本身的哲学是「默认零 JavaScript」，性能分数低不是框架的问题，而是我给每个页面都挂上了全站功能的账单。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;96 和 100 分说明了什么&lt;a href=&quot;#96-和-100-分说明了什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;先说好消息。可访问性 96 和 SEO 100 说明站点的骨架是健康的：语义化标签、标题层级、alt 文本、canonical、站点地图、RSS 这些结构性工作在定制主题时就已经做对了。这些是后期很难补的东西，性能反而是可以逐步偿还的债——这笔债的偿还顺序值得想清楚。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;处方清单&lt;a href=&quot;#处方清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;按优先级排列，前两项已经完成了第一批：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;脚本外置与按路由加载。&lt;/strong&gt; 首批优化已上线：共享脚本改为缓存复用，收紧了 eager 预加载。下一批是让外观面板、音乐、灯箱和特效脚本退到首次交互之后再初始化——它们没有理由参与首屏竞争。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;移动端默认关闭特效。&lt;/strong&gt; 水波纹和樱花在手机上默认不加载，并尊重系统的 &lt;code&gt;prefers-reduced-motion&lt;/code&gt; 设置。这既是性能问题，也是可访问性问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字体自托管。&lt;/strong&gt; 霞鹜文楷目前走公共 CDN，可用性和隐私都有第三方依赖；计划自托管并配好系统字体回退，保证字体加载失败时阅读不受阻。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建立预算。&lt;/strong&gt; 给 HTML、CSS、JS、图片分别设上限，把 Core Web Vitals 纳入回归检查，用固定的 Lighthouse 基线防止优化成果在下一次加功能时悄悄退回去。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;为什么不一口气全做完&lt;a href=&quot;#为什么不一口气全做完&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;性能优化最忌讳的是把所有手段同时上线：时钟、切页、外观记忆这些功能都依赖脚本初始化时序，一次性改动意味着出问题时无法定位是哪一步引起的。一次只动一层，验证功能没有回归，再做下一层——慢，但每一步都可回退。&lt;/p&gt;&lt;p&gt;这份体检的结论不是「56 分不及格」，而是「功能与首屏成本的账单第一次对上了数」。分数是基线，不是成绩单。下次体检，希望性能那一栏能有新数字可写。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>给静态博客加上中文搜索：Pagefind 接入实录</title><link>https://susuji.top/posts/pagefind-chinese-search/</link><guid isPermaLink="true">https://susuji.top/posts/pagefind-chinese-search/</guid><description>记录 Pagefind 在本站的接入过程：构建期索引、正文范围标注、中文分词，以及一个会让线上搜索直接 404 的路径陷阱。</description><pubDate>Tue, 08 Sep 2026 03:00:00 GMT</pubDate><content:encoded>&lt;p&gt;文章只有三篇的时候，「搜索」看起来是个多余的栏目：归档页一屏就能翻完。但静态博客的内容是慢慢长出来的，等文章多到归档页需要翻好几屏时再补搜索，就要重新审视内容结构了。所以建站之初，我把搜索当成和 RSS 一样的基础设施来对待。&lt;/p&gt;
&lt;p&gt;静态站点没有后端，搜索只有两条路：要么在浏览器里加载全部文章的 JSON 再用脚本过滤，要么在构建阶段预先建好索引。前者实现简单，但成本随内容线性增长，手机上会明显变慢；后者多一道构建工序，换来的是「打开搜索框时才付出成本」。本站选了后者，具体来说是 &lt;a href=&quot;https://pagefind.app/&quot; target=&quot;_blank&quot;&gt;Pagefind&lt;/a&gt;。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Pagefind 的工作方式&lt;a href=&quot;#pagefind-的工作方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Pagefind 的思路很干净：&lt;code&gt;astro build&lt;/code&gt; 结束后，对 &lt;code&gt;dist/&lt;/code&gt; 里的静态 HTML 再跑一次索引，索引产物写进 &lt;code&gt;dist/pagefind/&lt;/code&gt;，随站点一起部署。浏览器打开搜索页时，先加载一个很小的入口脚本，之后根据输入的关键词按需拉取对应的索引分片——文章越多索引越大，但单次查询下载的分片不会同步膨胀。&lt;/p&gt;&lt;p&gt;最关键的机制是「范围标注」：Pagefind 默认索引整个 &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;，而导航、侧边栏、页脚这些模板文字会污染结果。给文章正文容器加上 &lt;code&gt;data-pagefind-body&lt;/code&gt; 后，索引范围就只剩正文；标题可以再用 &lt;code&gt;data-pagefind-weight&lt;/code&gt; 提权，这样搜文章标题里的词时，正确的文章会排在前面。&lt;/p&gt;&lt;p&gt;本站的标注大致是这样的：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-pagefind-body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-pagefind-weight&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;10&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-pagefind-meta&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;title&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;文章标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-pagefind-body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;prose&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;正文内容……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;data-pagefind-meta=&quot;title&quot;&lt;/code&gt; 会让搜索结果卡片直接展示这条标题，而不是截取正文片段的开头。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;一个让线上搜索 404 的陷阱&lt;a href=&quot;#一个让线上搜索-404-的陷阱&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;接入过程里踩过一个很有代表性的坑：Pagefind 的 &lt;code&gt;--site&lt;/code&gt; 参数指向了错误的目录。&lt;/p&gt;&lt;p&gt;Pagefind 忠实地索引了那个目录，于是索引里出现了一批根本不存在的假路径，同时索引产物被输出到了不会随站点上传的位置。构建不报错、预览一切正常，直到部署上线，搜索框里输入任何词都是 404。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;静态构建里「产物位置」和「工具默认行为」不匹配时，往往不会有任何报错。涉及索引、站点地图这类后处理，验收一定要放在真实产物上，而不是本地预览。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;本站最终的解法是把 Pagefind 挪进了构建脚本：一个专门的 TS 脚本解析出真正的站点根目录，再以子进程方式运行 &lt;code&gt;pagefind --site &amp;lt;root&amp;gt;&lt;/code&gt;。这也是为什么项目文档反复强调正式构建必须用完整的 &lt;code&gt;pnpm build&lt;/code&gt;——直接跑 &lt;code&gt;astro build&lt;/code&gt; 会跳过包括 Pagefind 在内的全部后处理，拿到一个搜索失效的产物。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;中文搜索的表现&lt;a href=&quot;#中文搜索的表现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;中文是搜索的经典难题：英文按空格分词，中文没有。Pagefind 对中文提供了开箱即用的支持，页面声明 &lt;code&gt;lang=&quot;zh-CN&quot;&lt;/code&gt; 时会自动启用中文分词，索引阶段完成切分，不需要再引入外部分词库。&lt;/p&gt;&lt;p&gt;实测本站，直接搜「酥酥鸡」「Firefly」「Nginx」都能命中预期文章，正文里的长词也能拆出合理的词组结果。需要说明边界：它是分词匹配，不是语义搜索——搜同义表述不会命中，拼音和错别字也不会被纠正。对一个内容量在几百篇以内的个人博客，这个能力边界完全够用。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;验收清单&lt;a href=&quot;#验收清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;每次改动搜索相关配置后，我会过一遍这几项：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;构建产物里 &lt;code&gt;pagefind/pagefind.js&lt;/code&gt; 确实存在；&lt;/li&gt;
&lt;li&gt;搜文章标题、正文关键词、英文专有名词各一次，结果符合预期；&lt;/li&gt;
&lt;li&gt;搜一个不存在的词，空结果页正常显示而不是报错；&lt;/li&gt;
&lt;li&gt;从搜索结果点进文章，落地页 URL 正确。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;搜索是那种「平时感觉不到，坏了才发现」的功能，把它纳入构建验收清单，比事后排查便宜得多。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>在信息流之外，留一张自己的书桌</title><link>https://susuji.top/posts/a-desk-of-my-own/</link><guid isPermaLink="true">https://susuji.top/posts/a-desk-of-my-own/</guid><description>九月随笔：为什么在算法把内容送到眼前的时代，还要自己维护一个更新很慢的网站。</description><pubDate>Tue, 08 Sep 2026 01:00:00 GMT</pubDate><content:encoded>&lt;p&gt;九月了。早晨的风开始有凉意，写这篇随笔的时候，桌上的茶从热到温，再凉掉。博客上线两周，我在这里写完了第三类内容——前两篇是技术，这一篇是生活。开站时说好的「生活随笔」，今天补上第一篇。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;两种看内容的方式&lt;a href=&quot;#两种看内容的方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我每天在信息流里待的时间不短。手指往上一划，下一条内容就出现，不需要任何决定。算法很清楚我会在哪里停留，于是同类内容越来越密，停下来刷完一小时，却很难说出记住了什么。&lt;/p&gt;&lt;p&gt;信息流的姿势是「等」。内容自己来，来多少接多少。&lt;/p&gt;&lt;p&gt;而维护一个博客的姿势是「找」。想写一篇东西，得先真的弄懂它；想引用一个链接，得先确认它还在。所有出现在这个网站上的东西，都是我亲手放上去的。慢得多，但每一件的来历我都清楚。&lt;/p&gt;&lt;p&gt;建站那篇文章里我说，这个小站更像一张长期使用的书桌，而不是一份包装精美的简历。两周用下来，这个比喻越来越准了：桌子会乱，要定期收拾；有的抽屉暂时空着，也不必急着塞满东西。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;写作是思考的完成时&lt;a href=&quot;#写作是思考的完成时&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这两周最大的体会：想清楚和写清楚之间，隔着很远一段路。&lt;/p&gt;&lt;p&gt;好几段我以为已经理解的东西——比如 Pagefind 的索引机制、Nginx 的原子切换——一旦落到纸面要写给别人看，立刻露出含糊的地方。写技术文章时那些「写不下去」的卡顿，几乎总是准确的检测器：卡在哪里，哪里就没真的懂。&lt;/p&gt;&lt;p&gt;所以随笔也有同样的作用，只是对象换成了生活。一件小事在脑子里盘旋时没有形状，写成文字后它就有了边界：开始、经过、结束，以及它为什么值得记。信息流负责让内容穿过我，写作负责让一些东西留下来。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;小站长的小事&lt;a href=&quot;#小站长的小事&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个网站给我带来一些很小的快乐，小到说出来有点不好意思。&lt;/p&gt;&lt;p&gt;比如侧边栏的时间进度卡。年初立的目标到九月完成了多少，一月一周过去了多少，一眼可见。时间本来是抓不住的东西，被几个小方块变得具体了。&lt;/p&gt;&lt;p&gt;比如运行时长。页脚那个从建站时刻开始计数的数字，两周年倒计时会提醒我这个网站打算长期存在。大多数App 的图标换了又换，很少有什么东西以「年」为单位陪着你。&lt;/p&gt;&lt;p&gt;再比如 RSS。订阅器里安安静静躺着几个站，它们更新时会轻轻冒出一个数字，不打扰，也不缺席。这种节制的更新方式，本身就是对信息流的一种回答。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;桌子会一直摆在这里&lt;a href=&quot;#桌子会一直摆在这里&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我知道这个网站不会有太多访客，也无所谓。它首先是我为自己搭的桌子：技术的账记在一头，生活的账记在另一头。&lt;/p&gt;&lt;p&gt;如果你偶然路过，读到了这里——欢迎常来看看。可以用 &lt;a href=&quot;/rss/&quot;&gt;RSS&lt;/a&gt; 订阅，更新不勤，但每篇都是认真写的。愿你的九月，也有一张自己的书桌。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>把博客换成自己的样子：头像、Logo 与 Favicon 替换实录</title><link>https://susuji.top/posts/replacing-default-branding/</link><guid isPermaLink="true">https://susuji.top/posts/replacing-default-branding/</guid><description>从 Firefly 默认形象到自己的小鸡吉祥物：精灵图拆解、连通域抠图、一次渲染翻车，以及视觉验收的教训。</description><pubDate>Mon, 07 Sep 2026 19:10:00 GMT</pubDate><content:encoded>&lt;p&gt;用主题建站的代价之一，是全站带着别人的脸：Firefly 的默认头像、Logo、Favicon。交接文档里这件事一直排在待办的第一位——一个认真经营的博客，浏览器标签页上却挂着主题作者的图标，说不过去。今晚把这笔账结了，过程值得记录，包括一次翻车。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;第一版：手绘小鸡，被打回&lt;a href=&quot;#第一版手绘小鸡被打回&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我先用 SVG 手绘了一只小鸡：金黄的身体、珊瑚红鸡冠、菱形小嘴，放在和站点一致的蓝紫渐变上，配樱花点缀。自认为设计系统是完整的。&lt;/p&gt;&lt;p&gt;上线之后，站长看完只给了五个字的评价：&lt;strong&gt;“这个鸡太丑了”&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;不服气地看了一遍，确实丑。手绘矢量图形的”可爱”是极难伪造的东西——比例差一点、弧度差一点，可爱就变成廉价。这是这次替换教我的第一课：&lt;strong&gt;风格可以复刻，味道不行&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;一张精灵图&lt;a href=&quot;#一张精灵图&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;站长随后发来一张图，就是现在全站在用的小鸡。拿到手第一眼有点懵：2172×724，一个宽得不正常的尺寸。&lt;/p&gt;&lt;p&gt;写了个小脚本逐列扫描非白像素的密度，内容分布立刻清楚了：左侧约 0-40% 是一只正面站立的黄色小鸡，右侧是三只并排的白色小鸡——姿势各不相同。这不是一张”头像”，是一张&lt;strong&gt;精灵图&lt;/strong&gt;：一只主角，加一套三帧的走路动画。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;抠图：只删”与边缘连通的白色”&lt;a href=&quot;#抠图只删与边缘连通的白色&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;素材是白底，站点需要透明底。直接按”接近白色就删”会出事——小鸡身体内部就有白色高光。&lt;/p&gt;&lt;p&gt;解法是从图片四条边做连通域填充（flood-fill）：只有与边缘连通的白色才会被删除。小鸡有完整的黑色描边，把身体内部的白色围得严严实实，填充自然在描边处停下。黄色小鸡的白色高光、白色小鸡的整个身体，都是这样保下来的。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;判断一个”按颜色抠除”的方案是否安全，先问一句：要删的颜色，和要保留的颜色，中间有没有可靠的边界。有描边的平涂插画，边界就是描边本身。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;翻车：一个放大两百倍的嘴巴&lt;a href=&quot;#翻车一个放大两百倍的嘴巴&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;合成图标时出过一次真正的 bug：把小鸡缩放到圆角图标里，缩放系数多乘了一个系数，小鸡被放大了约两百倍——而绘制顺序里嘴巴在最上层，于是两张 Logo 渲染出来是两块一模一样的纯橙色方块。&lt;/p&gt;&lt;p&gt;更值得记录的是它是怎么漏过验收的。第一轮”视觉验收”时，我的提问方式是”这是一个圆角紫底的金色小鸡图标，请检查构图是否协调”——检查者顺着我的描述给出了通过。&lt;strong&gt;带着结论提问，就会得到结论。&lt;/strong&gt; 后来改成两道工序：先做像素采样（背景该是渐变、中心该是主体色、两张 Logo 哈希必须不同），再用中性提问（“客观描述你看到了什么”）复核。那次翻车就是这么被揪出来的。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;最终形态&lt;a href=&quot;#最终形态&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;确定方向其实只用了三轮：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;手绘版：紫渐变底 + 手绘小鸡 → 因丑退役；&lt;/li&gt;
&lt;li&gt;素材版：紫渐变底 + 站长的小鸡 → 好看，但”外面套的东西”多余；&lt;/li&gt;
&lt;li&gt;最终版：去掉所有底托，小鸡本体直接作为头像、Logo 和 Favicon，透明背景，明暗模式通吃。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;那三帧走路动画也没浪费——404 页现在有一只原地踏步的小鸡，配着”页面不存在”的文案，算是这个小站第一个彩蛋。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;替换清单&lt;a href=&quot;#替换清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;给同样用主题建站的朋友一份核对清单：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;头像：&lt;code&gt;profileConfig&lt;/code&gt; 里的 avatar，注意它同时出现在侧栏、关于页和分享海报里；&lt;/li&gt;
&lt;li&gt;导航栏 Logo：明暗模式可以用同一张透明图，省掉双份维护；&lt;/li&gt;
&lt;li&gt;Favicon：别只换浏览器标签那一张，主题往往还带一套按系统明暗切换的默认图标；&lt;/li&gt;
&lt;li&gt;默认分享图：无封面页面的 &lt;code&gt;og:image&lt;/code&gt; 回退，社交平台转发的第一印象全靠它。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;默认资源是主题欠你的债，早还早干净。而素材这件事上，站长自己挑的图，永远比生成的对味。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从 Firefly 到「酥酥鸡」：我的博客定制记录</title><link>https://susuji.top/posts/customizing-firefly-blog/</link><guid isPermaLink="true">https://susuji.top/posts/customizing-firefly-blog/</guid><description>记录我如何在保留 Firefly 能力的同时，清理演示内容、收敛功能并完成个人化配置。</description><pubDate>Wed, 26 Aug 2026 01:00:00 GMT</pubDate><content:encoded>&lt;p&gt;选择一个成熟主题的价值，不是省掉所有思考，而是把精力从“重复造基础设施”转移到“这个网站究竟应该是什么样”。&lt;/p&gt;
&lt;p&gt;我的博客以 &lt;a href=&quot;https://github.com/CuteLeaf/Firefly&quot; target=&quot;_blank&quot;&gt;Firefly&lt;/a&gt; 为起点。它提供了文章、归档、分类、标签、Pagefind 搜索、RSS、评论适配和多种扩展页面。真正开始定制后，我做的第一件事却不是继续加功能，而是先做减法。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;先让配置与内容一致&lt;a href=&quot;#先让配置与内容一致&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Firefly 的页面开关很丰富，演示项目也准备了动态、相册、友链、留言和追番等示例。如果没有真实数据却把入口全部打开，读者看到的只会是空页面或主题作者的演示内容。&lt;/p&gt;&lt;p&gt;因此，第一版只保留这些核心入口：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;首页；&lt;/li&gt;
&lt;li&gt;文章归档、分类与标签；&lt;/li&gt;
&lt;li&gt;搜索；&lt;/li&gt;
&lt;li&gt;关于页；&lt;/li&gt;
&lt;li&gt;RSS。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;其他页面继续保留代码能力，但通过 &lt;code&gt;siteConfig.pages&lt;/code&gt; 关闭。等到有真实内容和维护意愿时，再重新启用会更稳妥。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;页面隐藏不等于内容一定不会输出。像动态页这样的功能还可能生成 JSON 数据接口，所以演示文件也需要清理，不能只删导航链接。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;建立清晰的视觉方向&lt;a href=&quot;#建立清晰的视觉方向&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我没有完全重做主题，而是围绕统一的视觉目标调整现有系统：以蓝色为主色，扩大内容区域，使用更适合中文阅读的字体，并保留卡片边框与轻量阴影。&lt;/p&gt;&lt;p&gt;导航负责帮助读者快速抵达内容，侧边栏则承担氛围和辅助信息。时钟、每日一言、运行时间与樱花效果都可以存在，但它们不能抢过正文。动效和装饰最终都要服从同一个判断：是否会影响阅读、性能或移动端布局。&lt;/p&gt;&lt;p&gt;亮色与暗色模式也不能只检查首页。文章卡片、代码块、提示框、标签、页脚和移动端菜单都需要分别验证，否则很容易在某个局部出现对比度不足或背景突兀的问题。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;文章结构比页面数量更重要&lt;a href=&quot;#文章结构比页面数量更重要&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Firefly 使用 Astro Content Collections 管理文章。每篇文章可以使用 Markdown 或 MDX，Frontmatter 负责标题、发布日期、描述、封面、分类、标签、草稿和评论等元数据。&lt;/p&gt;&lt;p&gt;我选择让每篇文章拥有独立目录：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── customizing-firefly-blog/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── index.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── cover.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这样文章 URL 由目录名稳定决定，封面和后续插图也能与正文放在一起。Frontmatter 中只需使用相对路径：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./cover.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;本地图片会进入 Astro 的图像处理流程，再按照站点配置输出 WebP。相比随机图片 API，这种方式没有第三方可用性和防盗链风险，也更容易保持整站风格统一。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;“构建成功”要包含搜索&lt;a href=&quot;#构建成功要包含搜索&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这个项目的正式构建命令不是单独执行 Astro，而是：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;原因是完整脚本还会执行 Pagefind 等后处理。如果只运行 &lt;code&gt;pnpm exec astro build&lt;/code&gt;，页面可能看起来正常，但 &lt;code&gt;dist/pagefind/&lt;/code&gt; 不完整，部署后搜索就会失效。&lt;/p&gt;&lt;p&gt;我的本地验收至少包含以下项目：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;类型与内容 Schema 检查通过；&lt;/li&gt;
&lt;li&gt;完整生产构建零错误；&lt;/li&gt;
&lt;li&gt;首页、文章页、分类、标签和归档内容正确；&lt;/li&gt;
&lt;li&gt;中文关键词能够被 Pagefind 搜到；&lt;/li&gt;
&lt;li&gt;RSS、站点地图、404 和 canonical 地址正确；&lt;/li&gt;
&lt;li&gt;桌面与手机、亮色与暗色模式都没有布局溢出。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;保留主题能力，但不被主题牵着走&lt;a href=&quot;#保留主题能力但不被主题牵着走&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我仍然保留 Firefly 的 Logo、Favicon 和开源署名，也保留未来重新开启评论、友链或相册的可能。不过，第一版不会因为“主题支持”就默认公开所有功能。&lt;/p&gt;&lt;p&gt;这次定制最大的收获不是改了多少 CSS，而是逐渐明确了一条边界：配置应该服务真实内容，功能应该对应持续维护的承诺。先把三篇文章写好，比先摆出十个空栏目更重要。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>把 Astro 静态博客部署到 Nginx：一次可回滚的上线记录</title><link>https://susuji.top/posts/deploying-astro-blog-with-nginx/</link><guid isPermaLink="true">https://susuji.top/posts/deploying-astro-blog-with-nginx/</guid><description>从完整构建、静态路由和缓存，到 DNS、HTTPS、原子切换与回滚的一次上线复盘。</description><pubDate>Wed, 26 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Astro 静态博客的部署看起来很简单：构建，然后把 &lt;code&gt;dist&lt;/code&gt; 上传到服务器。但真正影响上线质量的，往往是这两个动作之间被省略的部分——搜索索引是否生成、路由是否正确、证书能否续期，以及出现问题时能不能迅速回到上一版。&lt;/p&gt;
&lt;p&gt;这篇文章记录我为本站采用的发布思路。示例中的域名和目录均为通用占位值，不对应真实服务器信息。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;先确认拿到的是完整产物&lt;a href=&quot;#先确认拿到的是完整产物&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;本地环境固定使用项目声明的 Node.js 与 pnpm 版本，安装依赖时遵循锁文件：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--frozen-lockfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type-check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;构建结束后，我不会只看退出码，还会检查几个关键文件：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/index.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/404.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/pagefind/pagefind.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/rss.xml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist/sitemap-index.xml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;其中 Pagefind 最容易被忽略。Astro 页面构建完成不代表搜索索引已经生成，正式上线必须使用项目完整的 &lt;code&gt;pnpm build&lt;/code&gt;。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;如果刚删除过文章，旧的 Astro 数据缓存仍可能引用已不存在的内容。此时应清理项目构建缓存和 &lt;code&gt;dist&lt;/code&gt; 后重新构建，而不是把不完整产物带到服务器。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;服务器只接收静态文件&lt;a href=&quot;#服务器只接收静态文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;站点采用本地构建、服务器托管的方式。服务器不需要安装 Node.js，也不上传源码、依赖目录或本地配置，只接收验证过的 &lt;code&gt;dist&lt;/code&gt; 内容。&lt;/p&gt;&lt;p&gt;为了避免直接覆盖正在服务的目录，我把每次产物放进独立版本目录：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/var/www/example.com/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── current -&amp;gt; releases/20260826-100000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── releases/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── 20260825-230000/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── 20260826-100000/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;上传和解压都发生在新目录。确认首页、404、搜索、RSS 与站点地图齐全后，再原子切换 &lt;code&gt;current&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;releases/20260826-100000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/example.com/current.next&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/example.com/current.next&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/example.com/current&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这种结构有两个好处：切换瞬间不会出现“上传到一半”的页面，而且上一版仍然存在，回滚只需把软链接重新指回旧目录。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;Nginx 要理解静态站点路由&lt;a href=&quot;#nginx-要理解静态站点路由&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Astro 会为文章生成目录形式的页面，因此 Nginx 的站点根目录指向 &lt;code&gt;current&lt;/code&gt;，并让路径依次匹配文件和目录：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;span&gt; ssl http2;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;example.com;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/var/www/example.com/current;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;index.html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; $&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;/ &lt;/span&gt;&lt;span&gt;=404&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;error_page &lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt; /404.html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;/404.html &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;internal&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;=404&lt;/code&gt; 很重要。若把所有未知路径都回退到首页，用户看似没有遇到错误，但搜索引擎和监控得到的状态码也是 200，真正的坏链接会被掩盖。&lt;/p&gt;&lt;p&gt;带哈希的 Astro 资源可以长期缓存，Pagefind 索引则保留较短缓存，方便文章更新后及时生效：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; ^~ &lt;/span&gt;&lt;span&gt;/_astro/ &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=404&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expires &lt;/span&gt;&lt;span&gt;1y;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Cache-Control &lt;/span&gt;&lt;span&gt;&quot;public, immutable&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; ^~ &lt;/span&gt;&lt;span&gt;/pagefind/ &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=404&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expires &lt;/span&gt;&lt;span&gt;1h&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Cache-Control &lt;/span&gt;&lt;span&gt;&quot;public, max-age=3600&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;^~&lt;/code&gt; 可以防止通用图片正则覆盖 &lt;code&gt;_astro&lt;/code&gt; 的一年缓存规则。这是一个很小的配置细节，却会直接改变封面等哈希资源的缓存时间。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;DNS 与 HTTPS 必须按顺序进行&lt;a href=&quot;#dns-与-https-必须按顺序进行&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Let’s Encrypt 的 HTTP 验证要求域名已经指向目标服务器。因此顺序应该是：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;先用 HTTP 配置验证新站点；&lt;/li&gt;
&lt;li&gt;修改根域名与 &lt;code&gt;www&lt;/code&gt; 的 DNS 记录；&lt;/li&gt;
&lt;li&gt;从权威 DNS 和公共解析器确认记录生效；&lt;/li&gt;
&lt;li&gt;申请同时覆盖两个域名的证书；&lt;/li&gt;
&lt;li&gt;启用 HTTPS，并将 HTTP 与 &lt;code&gt;www&lt;/code&gt; 跳转到正式根域名。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;证书签发成功并不等于结束。Nginx 中的 ACME 验证目录必须与 Certbot 续期配置一致，最后还要执行一次续期演练：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;certbot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renew&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dry-run&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;如果演练失败，就应该在上线阶段修复，而不是等证书临近过期时才发现问题。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;验收不止是“首页能打开”&lt;a href=&quot;#验收不止是首页能打开&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我的上线检查分成四层：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;内容：主页、文章、分类、标签、归档、搜索和关于页；&lt;/li&gt;
&lt;li&gt;协议：HTTP 跳转、&lt;code&gt;www&lt;/code&gt; 跳转、证书域名与有效期；&lt;/li&gt;
&lt;li&gt;静态资源：Pagefind、RSS、站点地图、封面和长期缓存；&lt;/li&gt;
&lt;li&gt;错误路径：不存在页面返回定制内容，同时保持真实 404 状态码。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;另外还要检查 canonical 与站点地图中的域名，避免页面已经上线，SEO 地址却仍指向开发环境或旧域名。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;回滚方案要在发布前存在&lt;a href=&quot;#回滚方案要在发布前存在&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;发布完成后不要马上删除旧版本。若新版本出现阻塞问题，可以把 &lt;code&gt;current&lt;/code&gt; 指回上一版并重新加载 Nginx；静态站点不涉及数据库迁移时，这种回滚通常很快。&lt;/p&gt;&lt;p&gt;只有在新版本稳定运行一段时间后，才清理更早的产物，并始终保留至少一个已经验证的旧版本。&lt;/p&gt;&lt;p&gt;这次上线让我更确定：静态站点降低了运行时复杂度，但没有消除发布工程。完整构建、独立版本、原子切换、真实 404、证书续期和可验证回滚，才共同组成一次真正完成的部署。&lt;/p&gt;&lt;/section&gt;</content:encoded></item></channel></rss>