static: Accept-Encoding q-values silently drop compression when client includes weights
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 11.2k
- Forks
- 899
- Avg merge
- 2d 24m
- Merged PRs (30d)
- 40
Description
Describe the bug
In src/runtime/internal/static.ts, the Accept-Encoding header is parsed by splitting on , and calling .trim() before looking up in EncodingMap:
const encodings = [
...encodingHeader
.split(",")
.map((e) => EncodingMap[e.trim() as keyof typeof EncodingMap])
.filter(Boolean)
.sort(),
"",
];
.trim() only strips surrounding whitespace — it does not strip the optional ;q=… weight from each token. The RFC 9110 §12.5.3 grammar for Accept-Encoding is:
Accept-Encoding = #( codings [ weight ] )
codings = content-coding / "identity" / "*"
weight = OWS ";" OWS "q=" qvalue
So a header value like Accept-Encoding: gzip;q=1.0, br;q=0.9 produces tokens "gzip;q=1.0" and "br;q=0.9" after the split. Neither matches any key in EncodingMap (gzip, br, zstd), so .filter(Boolean) removes both and encodings becomes [""] (uncompressed only). The client receives the uncompressed asset even though it explicitly advertised support for compressed encodings.
Steps to reproduce
# Start any nitro app with compressed public assets (e.g. .gz / .br files in public/)
curl -H "Accept-Encoding: gzip;q=1.0" http://localhost:3000/some-asset.js
# → 200 with uncompressed body; Content-Encoding header missing
Expected behaviour
The ;q=… parameter should be stripped before the EncodingMap lookup so clients that include quality weights still receive compressed responses:
.map((e) => EncodingMap[e.trim().split(";")[0].trim() as keyof typeof EncodingMap])
Affected versions
Reproduces on main (c1ff6ba6).
Additional context
Proxies such as nginx can rewrite Accept-Encoding headers to include explicit q-values (gzip;q=1, br;q=0.9), and some HTTP clients (Go's net/http, Python httpx, configured curl) send them as well. The issue also means q-value–based preference ordering is never respected — the current .sort() on file extensions (alphabetical .br < .gz < .zst) decides encoding preference unconditionally.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/runtime/internal/static.ts at the Accept-Encoding parsing shown in the issue, then reproduce with the provided curl command against an app containing compressed public assets. Verify that weighted encoding tokens are recognized and that a supported compressed asset is returned with the appropriate Content-Encoding header instead of falling back to an uncompressed response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100