nitrojs / nitrojs/nitro

static: Accept-Encoding q-values silently drop compression when client includes weights

Open Beginner friendly
#4,457 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v2 v3
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.