<?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>Semantics · XB Field Notes</title><link>https://hk.xbcatm.com/tags/semantics/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://hk.xbcatm.com/tags/semantics/index.xml" rel="self" type="application/rss+xml"/><item><title>HTTP methods are a promise about side effects</title><link>https://hk.xbcatm.com/notes/http-methods-are-a-promise-about-side-effects/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/notes/http-methods-are-a-promise-about-side-effects/</guid><description>The HTTP method names what a request may change; a static origin can answer most of them without pretending to have a stateful contract.</description><content:encoded>&lt;p&gt;A request method tells the server what the client is asking it to do. A read-only file origin has a narrow contract, but “static site” alone does not prove that proxies or imported handlers expose no write operations. Check the route actually serving the resource.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/http-methods.detail.en.svg" alt="A semantic matrix separates safe GET and HEAD from unsafe but idempotent PUT and DELETE. A scoped Caddy v2.10.2 file route accepts GET and HEAD and rejects other methods with 405 and Allow, while earlier handlers may differ. Safe methods can still produce incidental logs; idempotency does not promise identical statuses or authorize every retry. Any supplied HEAD Content-Length describes the corresponding GET with the same coding; omission may be legal. Method support is separate from CORS permission." width="720" height="2900"&gt;&lt;figcaption&gt;Separate requested state change from intended repeat effect, then inspect the actual route. The file-handler boundary is scoped; method support is separate from CORS permission.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="safe-and-idempotent-answer-different-questions"&gt;Safe and idempotent answer different questions&lt;/h2&gt;
&lt;p&gt;GET and HEAD are safe: the client does not request a state-changing action. Incidental logging, metrics, or accounting can still change server state. PUT and DELETE are idempotent in their defined semantics, but are not safe: repeating the same request is intended to have the same effect as performing it once. That does not require identical response statuses, and it does not make every retry after a network failure appropriate.&lt;/p&gt;
&lt;p&gt;For a read-only static origin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GET&lt;/strong&gt; retrieves a representation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; describes the corresponding GET without response content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OPTIONS&lt;/strong&gt; asks about the target’s communication options; a file handler may reject it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;POST / PUT / DELETE / PATCH&lt;/strong&gt; should not fall through to successful file serving unless a separate, intended handler implements their semantics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-caddy-file-handlers-method-boundary"&gt;A Caddy file handler’s method boundary&lt;/h2&gt;
&lt;p&gt;Caddy v2.10.2’s file-serving path accepts GET and HEAD and rejects other methods with &lt;code&gt;405&lt;/code&gt; and &lt;code&gt;Allow: GET, HEAD&lt;/code&gt;. Redirects, missing paths, error handlers, and other configured routes can answer earlier or differently, so this is not a universal response for every static-site URL.&lt;/p&gt;
&lt;p&gt;The earlier note recorded:&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;$ curl -s -o /dev/null -w &amp;#39;OPTIONS -&amp;gt; HTTP %{http_code}\n&amp;#39; -X OPTIONS https://www.xbcatm.com/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OPTIONS -&amp;gt; HTTP 405
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The original response log is not retained here, and this review did not reproduce that live result. The command reports only status; it does not establish that the required &lt;code&gt;Allow&lt;/code&gt; field was present. Inspect that field separately. &lt;code&gt;405&lt;/code&gt; means a known method is not supported for this target; &lt;code&gt;501 Not Implemented&lt;/code&gt; is the usual distinction when the server does not recognize or implement a method at all.&lt;/p&gt;
&lt;h2 id="head-parity-allows-documented-omissions"&gt;HEAD parity allows documented omissions&lt;/h2&gt;
&lt;p&gt;HEAD should describe the same selected representation as GET, while omitting content. Fields calculated only while generating the content may be omitted. If &lt;code&gt;Content-Length&lt;/code&gt; is present, it must describe the corresponding GET content, using the same content coding.&lt;/p&gt;
&lt;p&gt;The earlier note reported this comparison:&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;HEAD content-length: 15890
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET bytes read : 15890
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those historical numbers are not a fixed expected size or a current verification result. A missing HEAD length alone is legal; a wrong supplied length needs investigation. Follow the &lt;code&gt;head-request-parity&lt;/code&gt; note for paired probes. The downloadable &lt;code&gt;check-http-headers.sh&lt;/code&gt; helper inspects a GET response and does not perform this comparison automatically.&lt;/p&gt;
&lt;h2 id="reject-unintended-writes-without-hiding-the-contract"&gt;Reject unintended writes without hiding the contract&lt;/h2&gt;
&lt;p&gt;For a method recognized but disallowed on the target, return &lt;code&gt;405&lt;/code&gt; with &lt;code&gt;Allow&lt;/code&gt; listing the methods actually supported. The list is not necessarily restricted to safe methods on an application route. Do not return a plausible &lt;code&gt;200&lt;/code&gt; page for an ignored write request.&lt;/p&gt;
&lt;p&gt;If browser clients need CORS preflight, define that OPTIONS behavior explicitly; method support and CORS permission are separate decisions. Test write rejection only against an authorized disposable fixture rather than assuming a public endpoint is harmless because the site looks static.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&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;, safe/idempotent methods and HEAD/405/501 semantics, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods"&gt;MDN: HTTP request methods&lt;/a&gt;, method overview, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.se/docs/manpage.html"&gt;curl manual&lt;/a&gt;, method and response-inspection options, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/caddyserver/caddy/v2.10.2/modules/caddyhttp/fileserver/staticfiles.go"&gt;Caddy v2.10.2 file-server source&lt;/a&gt;, method rejection after file selection, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fetch.spec.whatwg.org/#cors-preflight-fetch"&gt;WHATWG Fetch Standard&lt;/a&gt;, CORS preflight processing, checked 2026-10-15.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>