Describe the problem
The static file server re-derives at boot and request time what the build already computed, and each gap between the two surfaces as a separate bug.
sirv resolves MIME types through its own bundled mrmime. Kit's additions in utils/mime.js reach the copy dev and preview share, but not the one adapter-node bundles, which is why #13753 stayed broken there after #16234. #16564 patches it at the response layer.
sirv sets Vary: Accept-Encoding whenever gzip/brotli are on, but builder.compress only compresses a fixed extension list, so every asset outside that list gets the header without having a variant. #15182 only covered precompress: false. The upstream fix, lukeed/sirv#173, has been open since January.
sirv guesses paths from patterns, so filenames ending in + 404 in production while resolving in dev (#11766, still reproduces on 3.0.2).
It also walks the output with totalist at startup to build a lookup Kit already has, and derives its ETag from mtime rather than content.
Describe the proposed solution
Record what the build knows and serve from it.
manifest.assets already lists the files, and manifest.mimeTypes already maps extension to type through the patched lookup. What's missing is which files builder.compress actually produced .gz/.br for, and a content hash for the ETag. Both are known at adapt time and currently discarded.
Serving then becomes a lookup and a stream. The type comes from the manifest, Vary is set only for files that have a variant, exact keys remove the path-guessing class of bug, and there is no startup walk.
sirv drops out as a consequence rather than as the goal, along with totalist and @polka/url. That also removes mrmime from adapter-node's output, where it is 13.1 KB of a 26.8 KB chunk, all of it a general-purpose table answering questions the build already answered. Exporting the server from @sveltejs/kit/node keeps adapter-node on one copy, since rolldown already holds that entry external.
Alternatives considered
Upstreaming each fix. lukeed/sirv#173 was opened in January by the person who hit the Kit bug and has not been reviewed; sirv last shipped in September 2025 with 12 PRs open.
Patching per symptom, which is what #15182 and #16564 do. Each reproduces a piece of sirv's behaviour outside sirv; #16564 has to re-add the text/html charset that sirv appends itself.
Vendoring sirv's 249 lines as-is. That fixes the MIME case but keeps the runtime re-derivation, so Vary and the ETag stay as they are.
Importance
nice to have
Describe the problem
The static file server re-derives at boot and request time what the build already computed, and each gap between the two surfaces as a separate bug.
sirvresolves MIME types through its own bundledmrmime. Kit's additions inutils/mime.jsreach the copy dev and preview share, but not the oneadapter-nodebundles, which is why #13753 stayed broken there after #16234. #16564 patches it at the response layer.sirvsetsVary: Accept-Encodingwhenevergzip/brotliare on, butbuilder.compressonly compresses a fixed extension list, so every asset outside that list gets the header without having a variant. #15182 only coveredprecompress: false. The upstream fix, lukeed/sirv#173, has been open since January.sirvguesses paths from patterns, so filenames ending in+404 in production while resolving in dev (#11766, still reproduces on 3.0.2).It also walks the output with
totalistat startup to build a lookup Kit already has, and derives its ETag from mtime rather than content.Describe the proposed solution
Record what the build knows and serve from it.
manifest.assetsalready lists the files, andmanifest.mimeTypesalready maps extension to type through the patched lookup. What's missing is which filesbuilder.compressactually produced.gz/.brfor, and a content hash for the ETag. Both are known at adapt time and currently discarded.Serving then becomes a lookup and a stream. The type comes from the manifest,
Varyis set only for files that have a variant, exact keys remove the path-guessing class of bug, and there is no startup walk.sirvdrops out as a consequence rather than as the goal, along withtotalistand@polka/url. That also removesmrmimefromadapter-node's output, where it is 13.1 KB of a 26.8 KB chunk, all of it a general-purpose table answering questions the build already answered. Exporting the server from@sveltejs/kit/nodekeepsadapter-nodeon one copy, since rolldown already holds that entry external.Alternatives considered
Upstreaming each fix. lukeed/sirv#173 was opened in January by the person who hit the Kit bug and has not been reviewed;
sirvlast shipped in September 2025 with 12 PRs open.Patching per symptom, which is what #15182 and #16564 do. Each reproduces a piece of
sirv's behaviour outsidesirv; #16564 has to re-add thetext/htmlcharset thatsirvappends itself.Vendoring
sirv's 249 lines as-is. That fixes the MIME case but keeps the runtime re-derivation, soVaryand the ETag stay as they are.Importance
nice to have