Search docs

Jump between documentation pages.

Compression middleware

Daloy ships a focused compression slice: a first-party compression() middleware that uses the web-standard CompressionStream API instead of a Node-only compression package.

ts
import { App, compression } from "@daloyjs/core";

const app = new App({ env: "production" });

app.use(compression());

What it compresses

The middleware negotiates br, gzip, and deflate from the request Accept-Encodingheader and the runtime codecs available through CompressionStream. Runtime support is probed once and cached. If the platform has no supported codec, the middleware becomes a silent no-op instead of breaking older runtimes.

The default minimumSize is 1024 bytes. Small responses are left alone, and Daloy also checks the compressed byte length after encoding. If compression made the payload larger, the original response is kept.

Security skip rules

Compression can become an oracle when secrets and attacker-controlled bytes share the same compressed response. Daloy keeps those guards built in rather than asking every app to remember the same list.

  • Skips responses with Set-Cookie.
  • Skips requests with Authorization.
  • Skips requests carrying session, CSRF, XSRF, __Host-, or __Secure- cookies.
  • Skips any response that already has Content-Encoding.
  • Skips non-GET / non-HEAD requests and non-2xx responses.
  • Skips already-compressed content types such as images, video, audio, archives, fonts, WebAssembly, and PDFs. image/svg+xml is carved back in because it is XML text.

Cache and ETag behavior

Every response that reaches compression() gets Vary: Accept-Encoding, even when Daloy decides not to compress that specific response. That keeps downstream caches keyed by the negotiation surface from the first response onward.

If a compressed response already has a strong ETag such as "abc", Daloy downgrades it to W/"abc". The ETag was computed over the upstream body, not the compressed wire bytes, so a weak validator is the honest one.

No compression level knob

CompressionStream uses the runtime default. Daloy refuses any compressLevel option at construction, including 6, because exposing the knob invites expensive level-9 compression for tiny byte savings under load.

ts
app.use(compression({
  minimumSize: 2 * 1024,
  encodings: ["gzip"],
  authCookieNames: ["tenant-auth"],
  excludeContentTypes: ["application/x-parquet"],
}));

Ordering

Register compression() after middleware that may add Set-Cookie, Content-Encoding, or ETag headers. Daloy runs onSend hooks in registration order, so the compression hook should see the final response headers before it decides whether to encode the body.