Repository navigation
Keep headers that describe the deployment off a publisher's pages - #1356
Open
jwrosewell wants to merge 27 commits into
Open
jwrosewell wants to merge 27 commits into
jwrosewell wants to merge 27 commits into
Conversation
`x-served-by` names the node that answered, and `x-cache` and `x-cache-hits` say how a cache treated the request. They arrive on a response from the publisher's origin or from a cache on the way to it. `strip_diagnostic_headers` removes the three from a response unless the request was for a path in the namespace it is given. `ROUTE_NAMESPACE` names `/_ts`, the prefix of every path this server answers itself. A path that only starts with the namespace's text, `/_tsx` for one, is not in it.
The finalize middleware of each adapter takes the request's path before the handler runs, and removes the three headers from the response unless the path is beneath `/_ts`. The removal comes before the finalize step, so a header of one of those names that an operator sets in `[response_headers]` is still sent. Each adapter has three tests of its own middleware. One sends a response naming its node for a publisher's page and finds the headers gone, one does the same for a path beneath `/_ts` and finds them kept, and one finds an operator's `X-Cache` on a publisher's page.
The response headers section of the configuration guide names the three headers and says where they are kept, and the changelog records the change.
The publisher fallback removes X-Served-By, X-Cache and X-Cache-Hits from whatever the origin sends, so an unrouted path beneath /_ts no longer hands them back. The code says deployment as the title does, ROUTE_NAMESPACE says what answers beneath it, and each adapter keeps one test, that the strip runs before finalize.
The header named the platform's version number of the service to anyone who asked for any page. The version is in the attestation document, under platform.serviceVersion, where a deployment is asked about itself, and the Axum, Cloudflare and Spin adapters never wrote the header.
The middleware action carries headers for the reader's response, which core writes on the response at each place a serve pass runs: the assembled copy of a stored page, the streamed copy and the inline copy of a page that is not stored. A fetch middleware that writes one is refused when the chain is planned, because what it leaves is stored for every reader and a header goes on one reader's response. The middleware context names the platform the deployment runs on, as the adapter reports it for the attestation evidence. The registry carries that report, each adapter tells it once at startup, and the attestation route reads the same report.
A deployment that wants the platform's service version on its pages names `service_version` in a `[[serve]]` entry, and X-TS-Version is written on every page the entry covers, with the version the adapter reports, and on nothing else. No section selects it, it runs in the serve phase alone, and a platform that reports no version gets no header. The stock list, the example and the configuration guide carry it, and the publisher's tests prove the header reaches a buffered and a streamed page.
Core's version_header module is removed, with its call from the finalize step of the Axum, Cloudflare and Spin adapters and from the Fastly adapter's health probe and its settings fallback, so no response carries x-ts-version unless a [[serve]] entry names the service_version middleware. That middleware now writes the git version the build compiled in from TRUSTED_SERVER__GIT_VERSION or local git as X-TS-Version, when the build knows one, and the version the adapter reports for the service as X-TS-Service-Version, when it reports one. HEADER_X_TS_FASTLY_VERSION, which nothing wrote, goes with the module. The health tests of the three adapters prove the probe carries no version, the middleware's own tests prove each header is written when its version is known and nothing when neither is, and the publisher tests prove both headers reach a buffered page and neither reaches a page no entry names the middleware for. The configuration guide, the getting started guide, the EdgeZero guide and the changelog say the same.
This was referenced Oct 15, 2026
This was referenced Oct 17, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
crates/trusted-server-core/srccrates/trusted-server-adapter-fastly/srccrates/trusted-server-adapter-axum/srccrates/trusted-server-adapter-spin/srccrates/trusted-server-adapter-cloudflare/srcdocs/guidecrates/trusted-server-modules/srctrusted-server.example.tomlcrates/trusted-server-adapter-axum/testsCHANGELOG.mdCloses
Closes #1273
Spec
Spec: diagnostic-headers
Test plan
CI on this pull request at 21b0bae passed all 32 checks.
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute serveChecklist
unwrap()in production code (clippy deniesunwrap_usedin CI)logmacros as AGENTS.md asks (notprintln!)Stacks on robots.txt from modules (#1355).
What changes
A response no longer carries
X-Served-By,X-CacheorX-Cache-Hits, unless this server answered a request for a path beneath/_ts.X-Served-ByX-CacheX-Cache-HitsNo adapter writes a version on a response. What the build was made from is in the attestation document (#1353), under
build, and the platform's version number of the service underplatform.serviceVersion, which is where a deployment is asked about itself. A deployment that wants the versions on its pages names core'sservice_versionmiddleware in a[[serve]]entry. The middleware writesX-TS-Version, the git version the build compiled in fromTRUSTED_SERVER__GIT_VERSIONor local git, when the build knows one, andX-TS-Service-Version, the version the adapter reports for the service, when it reports one, on every page the entry covers and on nothing else.How it works
/_ts. The publisher fallback removes the three from whatever the origin sends, on every path. The finalize middleware of each of the four adapters then removes them from every response outside the namespace. So a request for an unrouted/_ts/anything, which the fallback sends to the origin, does not get the origin's headers back./_tsand/_ts/permissionsare./_tsxand/_ts-archiveare a publisher's pages, and are stripped like any other.[response_headers]among them. An operator who wantsX-Cacheon every page can still set it there.service_versionis core's own middleware. No section selects it, an entry names it, and it writesX-TS-Version, the git version the build compiled in, andX-TS-Service-Version, the platform's version number of the service, each only when its version is known.Why
A response to a reader carries what the page needs and nothing about the deployment, so that the response stays as clean and free of clutter as it can be. The headers above describe how the site is built, to anyone who asks for any page, without that being a decision anyone made. Three arrive from the publisher's origin or a cache on the way to it, and the version was written by the adapter itself.
A question about a deployment has addresses of its own. The inspection pages (#1350) answer beneath
/_ts, and so does attestation (#1353) unless its endpoint is moved, and what each shows is the publisher's decision. The rule here is the same one. What describes the deployment is answered where a deployment is asked about itself, and is left off everything else. A deployment that has a reason to put the service version on its pages says so in its settings, with the middleware, and the decision is then its own.What breaks
X-CacheorX-Served-Byfrom a page fetched through this server, to see whether the origin's cache answered, no longer sees them. The origin still sends them, and they can be read at the origin.X-TS-Versionfrom a page no longer finds it, whichever adapter served the page and whichever version it carried. It reads the attestation document, wherebuildsays what the build was made from andplatform.serviceVersionis the platform's number, or the deployment namesservice_versionin a[[serve]]entry.How it was verified
Not exercised: a response the Fastly entry point sends outside the middleware, being the router's own
405and the two debug endpoints, is not stripped. It carries nothing from an origin.References #1273.