<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Testing · XB 现场笔记</title><link>https://hk.xbcatm.com/zh/tags/testing/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://hk.xbcatm.com/zh/tags/testing/index.xml" rel="self" type="application/rss+xml"/><item><title>静态网站的发布检查清单</title><link>https://hk.xbcatm.com/zh/notes/a-release-checklist-for-a-static-site/</link><pubDate>Thu, 30 Jul 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/notes/a-release-checklist-for-a-static-site/</guid><description>每个发布都值得一组小、快、针对性强、且会为真实原因失败的检查。</description><content:encoded>&lt;p&gt;静态网站发布很简单，但简单并不免除发布检查的需要。有用的检查足够小，能在每次发布运行，也足够具体，能为真实的理由而失败。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/release-checklist.detail.zh.svg" alt="四个编号阶段分别构建固定生成器版本的干净制品，要求 index.html、404.html、robots.txt、sitemap.xml 非空并复核链接，在待发布和公共路由测试确切制品，最后记录提交、生成器版本和下载校验和。Hugo 源码预览位于独立分支，会重新构建；HEAD 检查元数据，GET 支持正文比较。" width="720" height="2570"&gt;&lt;figcaption&gt;检查对象应是生成制品。干净输出不等于可复现；占位符搜索只是复核线索，真正的链接门禁应基于解析器。发布前服务确切制品，发布后比较两种语言的公共 GET 正文，并保存提交、生成器版本和下载校验和。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="构建干净的制品"&gt;构建干净的制品&lt;/h2&gt;
&lt;p&gt;使用固定版本的生成器，构建到一次性的输出目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo --cleanDestinationDir --gc --minify --destination public
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--gc&lt;/code&gt; 回收未使用的构建缓存资源，不会清理陈旧的已发布页面。&lt;code&gt;--cleanDestinationDir&lt;/code&gt; 会移除目标目录中的陈旧文件，因此只能用于一次性制品目录，不能指向源码或其他数据所在的目录。把打印出的版本与项目固定版本比较；打印版本本身不会强制版本一致。记录构建输入并验证生成的制品。干净的目标目录不能证明逐字节可复现：时间戳与其他非确定性输入需要单独控制和比较。不要在构建之后手改生成的 HTML。&lt;/p&gt;
&lt;h2 id="检查制品而不只是源码"&gt;检查制品，而不只是源码&lt;/h2&gt;
&lt;p&gt;检视生成的树，确认期望的入口点存在。把下面的断言块作为独立 shell 脚本运行；任何必需文件缺失或为空时，都应在打印清单之前停止：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; -s public/index.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; -s public/404.html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; -s public/robots.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; -s public/sitemap.xml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find public -type f -print &lt;span class="p"&gt;|&lt;/span&gt; sort
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后搜索意外的占位符、本地路径与仅开发用的主机：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -l &lt;span class="s1"&gt;&amp;#39;localhost|127\.0\.0\.1|TODO|example\.com|/Users/&amp;#39;&lt;/span&gt; public --glob &lt;span class="s1"&gt;&amp;#39;*.html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这只是复核线索，不是自动发布门禁：本清单这类示例本来就会命中，翻译页中也一样。这项仅检查 HTML 的扫描不会覆盖 XML 订阅或 JSON 搜索输出。&lt;code&gt;rg&lt;/code&gt; 无匹配时退出 1，发生错误时退出 2。逐个复核列出的文件，区分围栏示例与有效链接或属性，并用基于解析器的链接检查作为真正门禁。公开链接中的私有路径属于发布错误。&lt;/p&gt;
&lt;h2 id="验证公共路由"&gt;验证公共路由&lt;/h2&gt;
&lt;p&gt;让隔离的类生产服务器直接指向这份制品，才能进行更可靠的发布前路由检查。Hugo 开发服务器会重新构建内容；下面是独立的源码预览，不能证明它服务的就是已经检查过的 &lt;code&gt;public/&lt;/code&gt; 字节。第一条命令在一个终端运行，其余请求在另一个终端运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo server --bind 127.0.0.1 --port &lt;span class="m"&gt;1313&lt;/span&gt; --baseURL http://127.0.0.1:1313/ &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --disableFastRender --disableLiveReload --renderToMemory
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://127.0.0.1:1313/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://127.0.0.1:1313/notes/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://127.0.0.1:1313/resources/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://127.0.0.1:1313/missing-page/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;HEAD 检查不比较正文。对待发布服务器再运行 GET，发布后将公共正文与制品比较，并检查两种语言。确认缺失路由在生产服务器上返回 404 状态。一个外观精美但状态 200 的错误页，仍然会破坏监控与缓存。&lt;/p&gt;
&lt;h2 id="记录结果"&gt;记录结果&lt;/h2&gt;
&lt;p&gt;在发布记录中保留源码提交、生成器版本与每个可下载资源的校验和。这样无需依赖记忆，就能把线上文件与产生它的源码进行比较。&lt;/p&gt;
&lt;p&gt;这份检查清单的可下载版本在&lt;a href="https://hk.xbcatm.com/zh/resources/"&gt;资源&lt;/a&gt;一节中。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/commands/hugo/"&gt;Hugo command reference&lt;/a&gt;，干净生产构建的生成器标志，已核验 2026-07-30。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;，HTTP 验证与响应头检查选项，已核验 2026-07-30。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hk.xbcatm.com/zh/resources/"&gt;静态网站发布检查清单&lt;/a&gt;，可下载的配套资源。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/commands/hugo_server/"&gt;Hugo server command reference&lt;/a&gt;，开发服务器重新构建、内存渲染与实时刷新控制，已核验 2026-10-15。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>发布清单应点名每一个已发布的字节</title><link>https://hk.xbcatm.com/zh/notes/a-release-manifest-should-name-every-published-byte/</link><pubDate>Thu, 16 Jul 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/notes/a-release-manifest-should-name-every-published-byte/</guid><description>生成确定性的文件清单，并在传输前后比较。</description><content:encoded>&lt;p&gt;静态制品是一个目录，而不是单个可执行文件。只记录源码提交或首页校验和会留下缺口：缺失的图片、陈旧别名或变化的下载可能悄悄留下来。传输前，应冻结制品并枚举其中每个常规文件。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/release-manifest.detail.zh.svg" alt="冻结的常规文件制品被传输到目标端，两端分别生成位于制品树外的有序清单。目标清单中多出示意的 old.js 记录；两份清单汇入完整记录比较。另一条分支对源端清单做哈希以生成发布标识。逐项校验不能发现额外文件，字节一致也不能证明作者身份。" width="720" height="1990"&gt;&lt;figcaption&gt;测量前停止写入、拒绝符号链接和特殊文件，并将清单保存在制品树外。逐项校验发现缺失或变化的字节；重新生成并比较完整清单还可发现多余文件。对可信源端清单做哈希可得到紧凑标识，真实性与元数据仍需独立控制。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="生成稳定的列表"&gt;生成稳定的列表&lt;/h2&gt;
&lt;p&gt;从包含 &lt;code&gt;public/&lt;/code&gt; 的目录，在 Bash 中使用 GNU findutils 与 coreutils 运行以下命令。清单应保存在被测目录之外。发布只能包含目录和常规文件：先检查并拒绝符号链接或特殊文件，因为它们不在该清单的契约内。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;manifest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/release.sha256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; public
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;nonregular&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;find . ! -type d ! -type f -print -quit&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$nonregular&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; find . -type f -print0 &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C sort -z &lt;span class="p"&gt;|&lt;/span&gt; xargs -0 -r sha256sum --
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt; &amp;gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$manifest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; -s &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$manifest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;先排序文件名，再计算哈希。工具之间用 NUL 分隔路径，可保留空格、制表符与内嵌换行；GNU &lt;code&gt;sha256sum&lt;/code&gt; 会在普通可校验输出中转义特殊文件名。不要用 &lt;code&gt;awk '{print $2}'&lt;/code&gt; 解析该输出：文件名中可以合法包含空白。单独赋值会保留 &lt;code&gt;find&lt;/code&gt; 失败时的退出状态；只检查其输出，可能把扫描失败误认成空结果。扫描或管道一旦失败，就应把清单视为不完整，不得发布。测量期间停止写入；这些命令不会创建文件系统快照。&lt;/p&gt;
&lt;h2 id="传输后验证"&gt;传输后验证&lt;/h2&gt;
&lt;p&gt;在目标端的同一个可丢弃 Bash 会话中，按顺序运行后续片段，使路径变量与 &lt;code&gt;set&lt;/code&gt; 选项保持生效。将清单与制品路径设为实际位置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;manifest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/release.sha256
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;artifact&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/srv/www/site.new
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$artifact&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sha256sum -c &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$manifest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;检查列出的文件能发现缺失或改变的字节，却不能发现多余文件。因此按相同流程重新生成目标端清单，并比较完整记录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;observed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;manifest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.observed.XXXXXX&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rm -f &amp;#34;$observed&amp;#34;&amp;#39;&lt;/span&gt; EXIT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$artifact&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;nonregular&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;find . ! -type d ! -type f -print -quit&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$nonregular&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; find . -type f -print0 &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C sort -z &lt;span class="p"&gt;|&lt;/span&gt; xargs -0 -r sha256sum --
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt; &amp;gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$observed&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;diff -u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$manifest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$observed&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;没有差异且退出状态为零，表示常规文件的路径与哈希一致。多出一个陈旧文件也会使检查失败。临时输出位于制品之外，因此检查不会包含自身。空目录、权限、所有者与时间戳不在测量范围内；部署依赖这些属性时需另行验证。&lt;/p&gt;
&lt;h2 id="为发布记录增加聚合摘要"&gt;为发布记录增加聚合摘要&lt;/h2&gt;
&lt;p&gt;对清单本身做哈希，得到紧凑的发布标识：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256sum &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$manifest&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其含义依赖固定的规则：相对路径、排序区域、算法、工具格式，以及包含哪些元数据。将可信清单与源码提交、构建器版本一起保存。&lt;/p&gt;
&lt;h2 id="不要把完整性等同于真实性"&gt;不要把完整性等同于真实性&lt;/h2&gt;
&lt;p&gt;SHA-256 摘要一致，是字节一致的强实用证据，并不能证明作者身份。攻击者若能同时替换制品与清单，也能让检查通过。签名与可信分发是独立控制；只应校验通过预期可信渠道取得的清单。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.gnu.org/software/coreutils/manual/html_node/sha2-utilities.html"&gt;GNU Coreutils: sha2 utilities&lt;/a&gt;，校验和生成与验证；通过下列上游手册源码核对，已核验 2026-10-15。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/coreutils/coreutils/master/doc/coreutils.texi"&gt;GNU Coreutils 手册源码&lt;/a&gt;，非 tagged 校验和的转义、&lt;code&gt;--check&lt;/code&gt; 与 NUL 分隔排序，已核验 2026-10-15。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man1/sha256sum.1.html"&gt;sha256sum(1)&lt;/a&gt;，命令行为与输出格式，已核验 2026-07-16。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Simple-Command-Expansion.html"&gt;GNU Bash: Simple Command Expansion&lt;/a&gt;，纯赋值命令保留命令替换的退出状态，已核验 2026-10-18。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>发布在公共边缘一致之前不算完成</title><link>https://hk.xbcatm.com/zh/notes/a-release-is-not-complete-until-the-public-edge-agrees/</link><pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/notes/a-release-is-not-complete-until-the-public-edge-agrees/</guid><description>把生成制品与访问者实际能收到的响应进行比较。</description><content:encoded>&lt;p&gt;静态发布消除了应用运行时的复杂度，但不会消除生成目录与公共响应之间的边界。发布检查应同时覆盖两边。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/diagrams/release-path.svg" alt="一条小的静态发布路径" /&gt;
&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/release-edge.detail.zh.svg" alt="先在使用预期配置的待发布服务器上检查确切制品，切换目录后在公共边缘重复检查。将 index.html、下载资源和 404.html 与解码后的 GET 正文配对。字节比较与预期状态、类型、缓存、安全头分别检查。重复中英文样本，并在新目录通过检查前保留上一个已知良好目录。" width="720" height="2200"&gt;&lt;figcaption&gt;切换前后使用同一份制品。下载行对应文中的 public/downloads/check-http-headers.sh。cmp 比较前会解码受支持的内容编码；服务器转换需有明确比较规则。状态和响应头另行检查，并保留前一目录供主动回滚。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="从干净的制品开始"&gt;从干净的制品开始&lt;/h2&gt;
&lt;p&gt;使用固定生成器构建到干净的目录。记录源码提交、生成器版本、输出文件列表，以及用户会下载的文件的校验和。一次干净的构建能捕获复制式部署可能会意外保留的陈旧文件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find public -type f -print &lt;span class="p"&gt;|&lt;/span&gt; sort
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;文件列表是证据，不是装饰。如果某条路由消失，diff 应能说明是该来源被移除，还是生成器未能产出它。&lt;/p&gt;
&lt;h2 id="演练三类响应"&gt;演练三类响应&lt;/h2&gt;
&lt;p&gt;发布前，用预期服务器配置测试待发布制品。切换目录后，在公共边缘重复检查；探测仍在线的旧站不能验证新发布。请求一个 HTML 页面、一个静态资源与一个缺失路径。检查状态码、内容类型、缓存策略与安全头：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --compressed -fsS -D page.headers -o page.body https://www.example.test/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --compressed -fsS -D asset.headers -o asset.body &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://www.example.test/downloads/check-http-headers.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --compressed -sS -D missing.headers -o missing.body &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://www.example.test/does-not-exist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmp public/index.html page.body
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmp public/downloads/check-http-headers.sh asset.body
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmp public/404.html missing.body
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把每个片段作为独立 shell 脚本运行，或放在可丢弃的 shell 会话中执行。&lt;code&gt;set -eu&lt;/code&gt; 让这个顺序脚本在构建、传输或比较失败后停止，避免后面的成功命令掩盖先前失败。缺失路径的请求有意不用 &lt;code&gt;-f&lt;/code&gt;：404 是预期结果，仍应被捕获。检查各响应头文件的最终状态：依次为 &lt;code&gt;200&lt;/code&gt;、&lt;code&gt;200&lt;/code&gt;、&lt;code&gt;404&lt;/code&gt;；字节比较不会自动完成状态码检查。这里不跟随重定向。&lt;code&gt;--compressed&lt;/code&gt; 在比较前解码受支持的内容编码；若服务器有意转换 HTML，需要另行说明比较规则。&lt;code&gt;cmp&lt;/code&gt; 相等时不输出，有差异时返回非零。在 &lt;code&gt;/zh/&lt;/code&gt; 及其错误页上重复检查，不能直接假设两种语言一致。&lt;/p&gt;
&lt;h2 id="让回滚保持简单"&gt;让回滚保持简单&lt;/h2&gt;
&lt;p&gt;在新目录通过检查之前，不要删除上一个目录。回滚应选择一个已知良好目录，而不是在事故中凭记忆重新构造一个。&lt;a href="https://hk.xbcatm.com/zh/resources/"&gt;发布检查清单&lt;/a&gt;与&lt;a href="https://hk.xbcatm.com/zh/resources/"&gt;HTTP 响应头检查&lt;/a&gt;都刻意做得足够小，能在干净的 shell 里运行。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html"&gt;RFC 9110 — HTTP Semantics&lt;/a&gt;，状态与表示语义，已核验 2026-07-09。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;，响应检查选项，已核验 2026-07-09。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hk.xbcatm.com/zh/resources/"&gt;静态网站发布检查清单&lt;/a&gt;，本地配套资源。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>范围请求应证明选中了哪些字节</title><link>https://hk.xbcatm.com/zh/notes/range-requests-should-prove-which-bytes-were-selected/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/notes/range-requests-should-prove-which-bytes-were-selected/</guid><description>验证部分响应、Content-Range，以及无法满足的字节范围的行为。</description><content:encoded>&lt;p&gt;范围请求让客户端请求表示的一部分。媒体播放器、可断点续传的下载工具与诊断客户端会用到它，但服务器如果错误地声明或返回范围，就可能产生静默损坏，而不是干净的失败。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/range-request.detail.zh.svg" alt="稳定的编码后表示共 N 字节，提供参考大小。标尺选中含端点的 0 到 15，恰好 16 字节。分支区分匹配正文与 Content-Range 的 206、忽略 Range 时的完整 200，以及从 N 开始的越界请求被拒绝时的 416 和 bytes */N。强 If-Range 验证器防止拼接不同版本。禁止弱 ETag；只有无 ETag 且日期满足强验证器条件时才能使用 HTTP 日期。表示改变时须返回完整的最新正文。" width="720" height="3050"&gt;&lt;figcaption&gt;保持表示与内容编码一致，同时核对区间、总大小与实际正文字节。忽略 Range 时，完整的 200 可以合法；续传需要合适的强验证器。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id="建立完整大小"&gt;建立完整大小&lt;/h3&gt;
&lt;p&gt;选择至少 16 字节、测试期间不变的下载文件。在同一个一次性 shell 会话和临时工作目录中依次运行以下片段，保留 &lt;code&gt;url&lt;/code&gt; 与 &lt;code&gt;size&lt;/code&gt;。显式的 &lt;code&gt;set -eu&lt;/code&gt; 会让顺序执行的传输与字节断言失败时停止；HTTP 状态与范围响应头仍需按下文人工核对：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://www.example.test/downloads/archive.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsS -H &lt;span class="s1"&gt;&amp;#39;Accept-Encoding: identity&amp;#39;&lt;/span&gt; -D full.headers -o full.body &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wc -c &amp;lt; full.body&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$size&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -ge &lt;span class="m"&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;awk &lt;span class="s1"&gt;&amp;#39;tolower($1) ~ /^(content-length:|content-encoding:|etag:|last-modified:|accept-ranges:)$/ { print }&amp;#39;&lt;/span&gt; full.headers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确认完整响应为 &lt;code&gt;200&lt;/code&gt;，且没有意外的内容编码。这些命令请求 identity 表示，不做自动解压；字节范围针对所选的编码后表示，因此改变编码会使比较失效。&lt;code&gt;Content-Length&lt;/code&gt; 可能缺失，所以用完整正文确定大小。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Accept-Ranges: bytes&lt;/code&gt; 是显式信号，但缺少这个头并不能证明范围请求不受支持。去实际请求一次。&lt;/p&gt;
&lt;h3 id="请求一个已知区间"&gt;请求一个已知区间&lt;/h3&gt;
&lt;p&gt;请求开头 16 字节，同时保留响应头与正文：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -D range.headers -o range.body &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Accept-Encoding: identity&amp;#39;&lt;/span&gt; -H &lt;span class="s1"&gt;&amp;#39;Range: bytes=0-15&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wc -c range.body
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;head -c &lt;span class="m"&gt;16&lt;/span&gt; full.body &amp;gt; expected.body
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmp expected.body range.body
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;awk &lt;span class="s1"&gt;&amp;#39;tolower($1) ~ /^(content-range:|content-length:|etag:)$/ || /^HTTP\// { print }&amp;#39;&lt;/span&gt; range.headers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;成功的字节范围响应应使用 &lt;code&gt;206 Partial Content&lt;/code&gt;。&lt;code&gt;Content-Range&lt;/code&gt; 应指出 &lt;code&gt;0-15&lt;/code&gt; 与完整表示长度，而正文应正好包含 16 个字节。&lt;/p&gt;
&lt;p&gt;这里的字节比较专门检查部分正文。合法但大于 16 字节的完整 &lt;code&gt;200&lt;/code&gt; 会使比较失败并停止当前 shell 会话；先查看已保存的响应头，再判断是否发生损坏。反过来，完整文件恰好为 16 字节时，即便返回 &lt;code&gt;200&lt;/code&gt; 也可能通过 &lt;code&gt;cmp&lt;/code&gt;，所以仍需核对状态码与 &lt;code&gt;Content-Range&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="测试一个不可能的区间"&gt;测试一个不可能的区间&lt;/h3&gt;
&lt;p&gt;从完整字节数开始请求：大小为 N 的文件，最后一个有效偏移量是 N−1。与任意写一个大常量不同，这确实超出了已知表示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -eu
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -D - -o /dev/null &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Accept-Encoding: identity&amp;#39;&lt;/span&gt; -H &lt;span class="s2"&gt;&amp;#34;Range: bytes=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;服务器用 &lt;code&gt;416 Range Not Satisfiable&lt;/code&gt; 拒绝无法满足的字节范围时，应附带 &lt;code&gt;Content-Range&lt;/code&gt;（例如 &lt;code&gt;bytes */12345&lt;/code&gt;）报告当前大小。当服务器忽略 Range 时返回 &lt;code&gt;200&lt;/code&gt; 也可能合法，但它必须返回完整表示，而不是一个标签错误的片段。&lt;/p&gt;
&lt;h3 id="让验证器留在故事里"&gt;让验证器留在故事里&lt;/h3&gt;
&lt;p&gt;续传工具可以用 &lt;code&gt;If-Range&lt;/code&gt; 配合强 ETag。不能使用弱 ETag；只有在没有 ETag、且日期符合强验证器条件时，才能使用 HTTP 日期。如果表示已改变，安全响应是完整的最新表示，而不是从两个版本拼接出的字节。在宣称某个下载&amp;quot;可续传&amp;quot;之前，先测试这一行为。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-range-requests"&gt;RFC 9110 — HTTP Semantics&lt;/a&gt;，范围单位、部分响应与 &lt;code&gt;Content-Range&lt;/code&gt;，已核验 2026-07-06。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;，&lt;code&gt;--range&lt;/code&gt; 与响应头选项，已核验 2026-07-06。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>HEAD 应描述与 GET 相同的表示</title><link>https://hk.xbcatm.com/zh/notes/head-should-describe-the-same-representation-as-get/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/notes/head-should-describe-the-same-representation-as-get/</guid><description>比较 HEAD 与 GET 的元数据，不要把"缺少正文"误当成"资源不同"。</description><content:encoded>&lt;p&gt;&lt;code&gt;HEAD&lt;/code&gt; 方法请求服务器对 &lt;code&gt;GET&lt;/code&gt; 会返回的响应，但不会传输响应内容。它适合需要状态、验证器、类型或长度的检查，但前提是元数据与真实的 &lt;code&gt;GET&lt;/code&gt; 路径保持一致。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/head-parity.detail.zh.svg" alt="包括 Accept-Encoding 在内的相同请求条件进入 GET 与 HEAD 元数据卡片。只有 GET 连到正文和校验和。HEAD 缺少 Content-Length 不等于失败，提供时应描述对应 GET 内容。小 GET 检查有效载荷，并调查方法分歧与重定向比较范围。" width="720" height="2240"&gt;&lt;figcaption&gt;比较 HEAD 与 GET 之前，先对齐 URL、请求头、发布版本与内容编码。允许省略的字段不能自动判为失败。应保留小 GET 的正文或校验和检查，因为看似合理的元数据不能证明有效载荷完好。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="比较规范化后的响应头集合"&gt;比较规范化后的响应头集合&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Date&lt;/code&gt; 这类动态响应头会随请求不同而变化。只比较描述所选表示的那些字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://www.example.test/downloads/manual.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -I &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt; head.headers
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -D get.headers -o /dev/null &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; file in head.headers get.headers&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;== %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; awk &lt;span class="s1"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; /^HTTP\// ||
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; tolower($1) ~ /^(content-type:|content-length:|content-encoding:|cache-control:|etag:|last-modified:|vary:)$/ { print }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在可丢弃的工作目录运行，因为两个头部文件会被覆盖。对齐 URL、请求头与发布版本；压缩协商也应纳入比较，给两个请求发送相同的 &lt;code&gt;Accept-Encoding&lt;/code&gt; 值。&lt;/p&gt;
&lt;p&gt;HEAD 应描述对应的 GET，但 RFC 9110 允许省略只有在生成内容时才能确定的字段。单独缺少 &lt;code&gt;Content-Length&lt;/code&gt; 不代表失败；如果 HEAD 携带它，其值必须是对应 GET 内容的长度，不能因为 HEAD 没有正文就填零。比较时应统一内容编码，调查变化的验证器与类型，而不是要求两份头部逐字节相同。&lt;/p&gt;
&lt;h2 id="不要把-head-当作唯一的发布检查"&gt;不要把 HEAD 当作唯一的发布检查&lt;/h2&gt;
&lt;p&gt;服务器可能在文件缺失、不可读或被截断时仍合成看似合理的响应头。在冒烟测试里保留一个小的 &lt;code&gt;GET&lt;/code&gt; 并核验其正文或校验和。&lt;code&gt;HEAD&lt;/code&gt; 降低了传输成本，但并不能证明有效载荷存在。&lt;/p&gt;
&lt;h2 id="显式检查重定向"&gt;显式检查重定向&lt;/h2&gt;
&lt;p&gt;不使用 &lt;code&gt;--location&lt;/code&gt; 时，&lt;code&gt;curl -I&lt;/code&gt; 报告第一个响应，适合测试重定向策略；使用 &lt;code&gt;--location&lt;/code&gt; 时，会打印跟随过程中的多个响应头块，包括中间重定向与最终目标。请记录这条命令回答的是哪个问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -I --max-redirs &lt;span class="m"&gt;0&lt;/span&gt; https://old.example.test/manual
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS -I --location https://old.example.test/manual
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="把方法相关的失败视为配置缺陷"&gt;把方法相关的失败视为配置缺陷&lt;/h2&gt;
&lt;p&gt;一个对 &lt;code&gt;GET&lt;/code&gt; 返回 &lt;code&gt;200&lt;/code&gt;、对 &lt;code&gt;HEAD&lt;/code&gt; 返回 &lt;code&gt;405&lt;/code&gt; 或 &lt;code&gt;404&lt;/code&gt; 的静态页面，通常暴露出代理或路由的不一致。应当修复方法处理，而不是教监控系统忽略它——除非端点契约刻意不允许 &lt;code&gt;HEAD&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-head"&gt;RFC 9110 — HTTP Semantics&lt;/a&gt;，HEAD 方法语义，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;，&lt;code&gt;--head&lt;/code&gt;、&lt;code&gt;--include&lt;/code&gt; 与重定向选项，已核验 2026-10-15。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>