Under construction

This documentation is still in progress. Some information may be incomplete or out of date. Please check back later.

Docs/Compression

Internals

Compression

hj-compress encodes responses with gzip, brotli and zstd, chosen by server priority but always bounded by the client's Accept-Encoding. Buffered bodies encode in one shot; streamed bodies stay streaming; precompressed static siblings skip the work entirely; and a trusted proxy can be handed forced zstd. The reverse direction — request decompression — is bounded and mostly opt-in.

Codecs and negotiation

CodecBackendDefault levelXML switches
zstdzstd3 (1–22)enableZstdCompress, enableDynZstdCompress, zstdCompressLevel
brotlibrotli5 (0–11)enableBrCompress, enableDynBrCompress, brCompressLevel
gzipflate26enableGzipCompress, enableDynGzipCompress
noyesyesnozstdbrgzipnoneBody::FullBody::Stream (proxy, SSE,LSAPI)static file with .br/.gzsibling

response body

compressible?
type in compressibleTypes · > 200 B · not already encoded · not 206

identity

trusted proxy peer and --cf-send-zstd?

zstd (forced)

first server-priority codec the client's
Accept-Encoding allows

zstd (level 3)

brotli (level 5)

gzip (level 6)

body shape

encode in one shot

CompressStream: per-chunk, never buffered

serve the precompressed sibling (br preferred)

Choosing an encoder. Bodies of 200 bytes or fewer are never compressed (OpenLiteSpeed parity), the compressible-type set matches LiteSpeed's compressibleTypes, already-encoded and 206 responses are skipped, and every encoded response carries Vary: Accept-Encoding.
diagram source (mermaid)
flowchart TB
  classDef c fill:#13203a,stroke:#818cf8,color:#fff
  A["response body"] --> B("compressible?<br/>type in compressibleTypes · > 200 B · not already encoded · not 206")
  B -- no --> ID["identity"]
  B -- yes --> T("trusted proxy peer and --cf-send-zstd?")
  T -- yes --> Z["zstd (forced)"]:::c
  T -- no --> N("first server-priority codec the client's<br/>Accept-Encoding allows")
  N -- zstd --> Z2["zstd (level 3)"]:::c
  N -- br --> BR["brotli (level 5)"]:::c
  N -- gzip --> GZ["gzip (level 6)"]:::c
  N -- none --> ID
  Z & Z2 & BR & GZ --> S("body shape")
  S -- "Body::Full" --> E1["encode in one shot"]
  S -- "Body::Stream (proxy, SSE, LSAPI)" --> E2["CompressStream: per-chunk, never buffered"]
  S -- "static file with .br/.gz sibling" --> E3["serve the precompressed sibling (br preferred)"]

zstd encoding uses a per-thread ZSTD_CCTX pool so no context is allocated per response. Streamed bodies (Body::Stream — LSAPI, proxy, SSE) wrap in a lazy per-chunk CompressStream: each inbound chunk is encoded as it arrives, so SSE stays real-time and a slow backend never holds the first byte hostage.

Trusted-proxy zstd egress

--cf-send-zstd (or CF_SEND_ZSTD=1) forces zstd to a trusted-proxy peer regardless of the forwarded Accept-Encoding. Cloudflare asks the origin for br/gzip but decodes an origin Content-Encoding: zstd and re-encodes per browser at its edge, so this hands the CDN the cheapest-to-produce form (zstd ≪ brotli to compress) without changing what browsers receive. Off by default, officially unsupported CDN↔origin, enabled in production; it never applies to untrusted or direct clients.

Precompressed siblings

If a .br or .gz sibling of a static file exists in the same directory, httpjet serves it directly with the matching Content-Encoding and Vary: Accept-Encoding — gzip_static/brotli_static behavior, brotli preferred. Ranged requests skip the swap so byte ranges stay exact; same-directory placement keeps accessDenyDir and symlink verdicts unchanged; a missing sibling falls back to on-the-fly compression at the cost of one stat.

Interaction with the page cache

The page cache keeps identity bytes plus one best precompressed variant chosen under the same priority; a client whose acceptable set differs is re-encoded per serve. Internal dictionary compression is a storage optimization only — dictionaries never reach the wire, and a hit decodes to identity before standard client compression is selected.

Request decompression

httpjet decodes a single supported Content-Encoding after the transport has fully framed the request body and before rewrite, cache, WAF, proxy or application dispatch — the same decoder for HTTP/1.1, HTTP/2, h2c and HTTP/3. Gzip is on by default for compatibility; brotli and zstd are opt-in:

serve · opt-in codings
httpjet --root /abs/root serve … --request-decompression-extra br,zstd     # accepts br, brotli, zstd; "gzip" is rejected as redundant
no (list, repeated)gzipbr / zstdnoyescannotokmalformedmaxReqBodySizehits 16 MiB outputceilingok

framed request body
(H1 · H2 · h2c · H3)

exactly one Content-Encoding field
with exactly one coding?

forward unchanged

decode (buffered)
zstd window ≤ 2^24 · no brotli large-window

listed in --request-decompression-extra?

reserve 17 MiB codec workspace
+ encoded + decoded bytes in the body budget

503

413

drop Content-Encoding, set decoded Content-Length
→ rewrite · WAF · cache · backend

Bounded, buffered, fail-safe. Only exactly one coding is decoded; malformed streams and the 16 MiB ceiling forward the original representation unchanged (the destination applies its own policy); the codec workspace and both body forms are charged to the shared budget first.
diagram source (mermaid)
flowchart TB
  classDef bad fill:#2a1420,stroke:#e879f9,color:#fff
  A["framed request body<br/>(H1 · H2 · h2c · H3)"] --> B("exactly one Content-Encoding field<br/>with exactly one coding?")
  B -- "no (list, repeated)" --> F["forward unchanged"]
  B -- gzip --> D
  B -- "br / zstd" --> C("listed in --request-decompression-extra?")
  C -- no --> F
  C -- yes --> R("reserve 17 MiB codec workspace<br/>+ encoded + decoded bytes in the body budget")
  R -- "cannot" --> E503[503]:::bad
  R -- ok --> D["decode (buffered)<br/>zstd window ≤ 2^24 · no brotli large-window"]
  D -- "malformed" --> F
  D -- "> maxReqBodySize" --> E413[413]:::bad
  D -- "hits 16 MiB output ceiling" --> F
  D -- ok --> G["drop Content-Encoding, set decoded Content-Length<br/>→ rewrite · WAF · cache · backend"]
tests
cargo test --locked -p httpjet uring::request_body --no-fail-fast     # all three codecs, bombs, malformed input, stacked encodings, workspace exhaustion

Decode safety

Response-side decoding (page-cache variants, dictionary frames) caps windowLogMax and bounds decoded output size; a hostile or corrupt frame fails the decode rather than ballooning memory. Compression cost depends on body size, codec, cache state and backend latency — compare representative hits and misses before trading CPU for bytes. In production the origin's compression phase is sampled as httpjet_phase_compress_seconds.

httpjet · documentation · synced to the source tree on 2026-10-13 (v0.0.5).
Source: github.com/faratech/httpjet · Home: httpjet.net · Found an error? Open an issue on the repository.