<?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>Caddy · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/caddy/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/caddy/index.xml" rel="self" type="application/rss+xml"/><item><title>安全响应头应描述实际页面</title><link>https://www.xbcatm.com/zh/notes/security-headers-should-describe-the-actual-page/</link><pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/notes/security-headers-should-describe-the-actual-page/</guid><description>当紧凑的策略与静态页面真正加载的资源匹配时，它更安全。</description><content:encoded>&lt;p&gt;安全响应头不是安全代码的替代品，但它们让浏览器强制实施有用的边界。策略应描述实际被服务的页面，而不是从另一个项目复制来的泛型模板。&lt;/p&gt;
&lt;h2 id="从资源图开始"&gt;从资源图开始&lt;/h2&gt;
&lt;p&gt;如果站点只提供 HTML、本地样式表、一个 favicon、没有任何可执行 JavaScript，那么一条窄策略是可理解的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Content-Security-Policy: default-src &amp;#39;self&amp;#39;; base-uri &amp;#39;self&amp;#39;; form-action &amp;#39;none&amp;#39;; frame-ancestors &amp;#39;none&amp;#39;; object-src &amp;#39;none&amp;#39;; script-src &amp;#39;none&amp;#39;; style-src &amp;#39;self&amp;#39;; img-src &amp;#39;self&amp;#39; data:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;script-src 'none'&lt;/code&gt; 只有在站点不需要可执行脚本时才是正确的选择。加入搜索、评论或统计标签会改变资源图，并应触发一次有意识的策略复核。&lt;/p&gt;
&lt;h2 id="增加传输与嵌套保护"&gt;增加传输与嵌套保护&lt;/h2&gt;
&lt;p&gt;对仅 HTTPS 的站点，下列响应头是常见起点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Strict-Transport-Security: max-age=31536000; includeSubDomains
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;X-Content-Type-Options: nosniff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;X-Frame-Options: DENY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Referrer-Policy: strict-origin-when-cross-origin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只有当每个子域名都准备好提供 HTTPS 时才启用带 includeSubDomains 的 HSTS。响应头是与未来部署的契约，而不只是扫描器里的一个分数。&lt;/p&gt;
&lt;h2 id="检查行为而不只是存在"&gt;检查行为，而不只是存在&lt;/h2&gt;
&lt;p&gt;请求一个正常页面、一个缺失页面与一个静态资源。错误响应应携带与成功响应同样重要的安全响应头。还要确认压缩与缓存不会改变内容类型，也不会暴露内部路径。&lt;/p&gt;
&lt;p&gt;本站的&lt;a href="https://www.xbcatm.com/zh/resources/"&gt;Caddy 示例&lt;/a&gt;只是一个起点，不是普适策略。请根据你自己的部署调整主机名、证书处理与应用路由。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP"&gt;MDN: Content Security Policy guide&lt;/a&gt;，CSP 指令名称与语法，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security"&gt;MDN: &lt;code&gt;Strict-Transport-Security&lt;/code&gt;&lt;/a&gt;，HSTS 指令与部署注意事项，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/caddyfile/directives/header"&gt;Caddy &lt;code&gt;header&lt;/code&gt; 指令&lt;/a&gt;，响应头操作指令，已核验 2026-08-22。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>静态服务器应有一个明确的根目录</title><link>https://www.xbcatm.com/zh/notes/a-static-server-should-have-one-obvious-root/</link><pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/notes/a-static-server-should-have-one-obvious-root/</guid><description>让 Caddy 文件服务、生成制品与回滚路径保持一致。</description><content:encoded>&lt;p&gt;静态部署最有用的属性是：运维人员不用读应用代码，就能回答&amp;quot;这条响应是哪个目录提供的？&amp;quot;。Caddy 的 &lt;code&gt;root&lt;/code&gt; 与 &lt;code&gt;file_server&lt;/code&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;www.example.test {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; root * /srv/example-site
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; encode zstd gzip
&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; header {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -Server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; X-Content-Type-Options nosniff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; X-Frame-Options DENY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Referrer-Policy strict-origin-when-cross-origin
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file_server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;请求一个 HTML 页面、一个可下载的文本文件和一个缺失路径。把内容类型与状态码与本地制品比较。即使 404 页看起来很精致，若返回 &lt;code&gt;200&lt;/code&gt; 仍是部署缺陷。&lt;/p&gt;
&lt;p&gt;当 Caddy 管理 HTTPS 时，证书签发与 HTTP 到 HTTPS 的重定向都属于边缘行为。请从裸的 HTTP 监听器测试重定向，并从公共主机名读取证书名称。不要只凭进程健康就推断成功。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/caddyfile/directives/file_server"&gt;Caddy &lt;code&gt;file_server&lt;/code&gt; 指令&lt;/a&gt;，静态文件服务与根目录选择，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/caddyfile/directives/header"&gt;Caddy &lt;code&gt;header&lt;/code&gt; 指令&lt;/a&gt;，响应头配置，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/automatic-https"&gt;Caddy Automatic HTTPS&lt;/a&gt;，证书签发与 HTTPS 重定向，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options"&gt;MDN: &lt;code&gt;X-Frame-Options&lt;/code&gt;&lt;/a&gt;，防止被嵌套的选项，已核验 2026-08-22。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>压缩会改变所选中的 HTTP 表示</title><link>https://www.xbcatm.com/zh/notes/compression-changes-the-selected-http-representation/</link><pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/notes/compression-changes-the-selected-http-representation/</guid><description>把内容编码、验证器与 Vary 视为同一个可观察的响应契约。</description><content:encoded>&lt;p&gt;压缩节省传输字节，但也制造了资源的另一种表示。客户端用 &lt;code&gt;Accept-Encoding&lt;/code&gt; 声明可接受的内容编码；服务器用 &lt;code&gt;Content-Encoding&lt;/code&gt; 描述实际应用的编码。&lt;/p&gt;
&lt;h3 id="让协商可见"&gt;让协商可见&lt;/h3&gt;
&lt;p&gt;当响应可能随 &lt;code&gt;Accept-Encoding&lt;/code&gt; 变化时，把该请求字段写进 &lt;code&gt;Vary&lt;/code&gt;。这告诉共享缓存：压缩响应与 identity 响应不是可互换的缓存条目。&lt;/p&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;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; https://www.example.test/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS --compressed -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; https://www.example.test/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;检查 &lt;code&gt;Content-Encoding&lt;/code&gt;、&lt;code&gt;Vary&lt;/code&gt;、&lt;code&gt;Content-Length&lt;/code&gt;（如存在）与验证器。服务器可为不同编码的表示使用不同实体标签；重要的是条件请求一致地验证同一个所选表示。&lt;/p&gt;
&lt;h3 id="压缩合适的内容"&gt;压缩合适的内容&lt;/h3&gt;
&lt;p&gt;HTML、CSS、JSON、XML、SVG 等文本格式通常适合压缩；已经压缩过的格式未必适合。Caddy 的 &lt;code&gt;encode&lt;/code&gt; 指令会协商支持的编码，并且通常对没有合适内容类型或体积过小的响应不做压缩。&lt;/p&gt;
&lt;p&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"&gt;RFC 9110 — HTTP Semantics&lt;/a&gt;，内容编码与 &lt;code&gt;Vary&lt;/code&gt;，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/caddyfile/directives/encode"&gt;Caddy &lt;code&gt;encode&lt;/code&gt; 指令&lt;/a&gt;，内容编码选择与协商，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;，&lt;code&gt;--compressed&lt;/code&gt;，已核验 2026-08-22。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>证书过期是一个日期，而不是仪表盘上的颜色</title><link>https://www.xbcatm.com/zh/notes/certificate-expiry-is-a-date-not-a-dashboard-color/</link><pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/notes/certificate-expiry-is-a-date-not-a-dashboard-color/</guid><description>检查真实主机名服务的证书，并把剩余天数变成可行动的阈值。</description><content:encoded>&lt;p&gt;HTTPS 进程可能是健康的，而公共证书却临近过期、为错误的名字签发，或在另一条边缘上被不同地提供。一条有用的检查，是向公共端点索取它会给真实客户端的那张证书。&lt;/p&gt;
&lt;h2 id="包含主机名"&gt;包含主机名&lt;/h2&gt;
&lt;p&gt;TLS 虚拟主机依赖服务器名称。把主机名作为 SNI 传入，并检视对端证书：&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;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;www.example.test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="s2"&gt;:443&amp;#34;&lt;/span&gt; -servername &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;lt;/dev/null 2&amp;gt;/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; &lt;span class="p"&gt;|&lt;/span&gt; openssl x509 -noout -subject -issuer -dates -fingerprint -sha256
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这条命令是只读的。它不会续签任何东西，也不能证明每个中间证书都被每个客户端接受。但它确实给出为该主机名呈现的证书的 not-before、not-after、subject、issuer 与指纹。&lt;/p&gt;
&lt;h2 id="在阈值上告警"&gt;在阈值上告警&lt;/h2&gt;
&lt;p&gt;选择能留出续期失败、DNS 问题与人工响应时间的阈值。确切数字属于服务的恢复目标；并非普通的&amp;quot;30 天&amp;quot;规则就能替代这个决定。配套的&lt;a href="https://www.xbcatm.com/zh/resources/"&gt;TLS 过期检查&lt;/a&gt;会打印过期时间，并在剩余区间低于你传入的阈值时以非零退出。&lt;/p&gt;
&lt;p&gt;尽可能从主机外部运行检查。本地进程看到的监听器、证书文件或代理路径，可能与公共网络上的访问者不同。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/automatic-https"&gt;Caddy Automatic HTTPS&lt;/a&gt;，自动 TLS 证书管理，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openssl.org/3.0/man1/openssl-s_client/"&gt;OpenSSL &lt;code&gt;s_client&lt;/code&gt; 文档&lt;/a&gt;，SNI 与证书检查选项，已核验 2026-08-22。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openssl.org/3.0/man1/openssl-x509/"&gt;OpenSSL &lt;code&gt;x509&lt;/code&gt; 文档&lt;/a&gt;，证书解析、日期与指纹，已核验 2026-08-22。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>