Library / SDK
expressjs/compression avatar
expressjs/compression

expressjs/compression: gzip, deflate and Brotli middleware for Express

Node.js compression middleware

2,805 stars255 forksJavaScriptMIT

At a glance

What is it?
A middleware that compresses Express response bodies with zlib or Brotli, chooses the coding from Accept-Encoding, and skips responses it cannot safely transform. Useful for teams serving text-heavy APIs and pages, provided they understand the threshold and filter behaviour.
Who is it for?
Adopt expressjs/compression if you run an Express or Connect-style server and your responses are mostly text, JSON or HTML that benefit from gzip or Brotli. Skip it if your payloads are already compressed images, video or archives, or if a CDN or reverse proxy in front of your app already negotiates encodings, since stacking both wastes CPU.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 18 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem expressjs/compression solves for Express servers

An Express route handler returns a string or an object, and Express writes it to the socket as-is. Nothing in the framework negotiates an encoding with the client, so a 200 KB JSON payload leaves the server at 200 KB even when the browser sent Accept-Encoding: gzip. This middleware sits in the request path and changes that: it inspects the request's Accept-Encoding header, picks a supported coding, and wraps the response stream so the body is compressed as it is written. The README lists three supported codings: deflate, gzip and br (Brotli), and notes Brotli is available in all currently supported Node.js LTS versions (v18+).

The audience is narrow but large: anyone running an Express or Connect-compatible server who wants compression without hand-rolling zlib plumbing per route. It is not a general HTTP toolkit. It does one thing, and the repository layout reflects that: the published package contains LICENSE, README.md and index.js.

How the middleware decides what to compress

The middleware runs on every request that passes through it, but it does not compress blindly. Three mechanisms decide the outcome.

First, negotiation. The negotiator dependency reads the request's Accept-Encoding header and the middleware selects a coding the client accepts. If the client sends no encoding, the enforceEncoding option (default identity) supplies the fallback.

Second, a filter function. The default filter uses the compressible module to check res.getHeader('Content-Type'). If the type is not recognised as compressible, the response is skipped. You can replace this with your own function, called as filter(req, res), returning true to consider the response or false to leave it alone.

Third, a size threshold. The threshold option defaults to 1kb and accepts a number of bytes or any string the bytes module accepts. The README is explicit that this is only an advisory setting: if the response size cannot be determined when the headers are written, the middleware assumes the response is over the threshold. Setting a Content-Length header is what makes the check meaningful.

There is also a hard opt-out. The README states the middleware will never compress responses that include a Cache-Control header with the no-transform directive, because compressing would transform the body. That is a correct reading of RFC 7234, and it means a route that sets that directive silently loses compression.

Installing compression and wiring it into an Express app

The README gives a single install command through the npm registry. Run it in your project directory:

bash
$ npm install compression

In the application, the module is required and registered with app.use. The README's Express example registers it as high in the middleware stack as you like, and states that requests passing through the middleware will be compressed:

js
var compression = require('compression')
var express = require('express')

var app = express()

app.use(compression())

After this, a request with Accept-Encoding: gzip to a route returning a compressible content type should come back with a Content-Encoding header and a smaller body. The README does not print the expected response headers, so check them yourself with curl -H 'Accept-Encoding: gzip' -I against one of your routes.

For a custom policy, the README shows extending the default filter rather than replacing it. This example skips compression when the request carries an x-no-compression header and otherwise defers to compression.filter:

js
app.use(compression({ filter: shouldCompress }))

function shouldCompress (req, res) {
  if (req.headers['x-no-compression']) {
    return false
  }

  return compression.filter(req, res)
}

One method comes with the middleware rather than from Express: res.flush(), which the README describes as forcing a partially-compressed response to be flushed to the client. That matters for server-sent events and streaming routes, where buffering inside the compressor would otherwise delay output.

Where compression middleware is the wrong tool

The most common mistake is enabling this in front of content that is already compressed. JPEG, PNG, MP4 and zip files gain nothing from a second pass through zlib, and the CPU spent on them is pure loss. The default filter mitigates this by consulting the compressible module, but the filter only sees the Content-Type header. If a route streams a zip file while declaring an application/json content type, the middleware will happily compress it.

The threshold is the second trap. Because the README describes it as advisory and says an undetermined size is treated as over the threshold, a streaming route without Content-Length gets compressed regardless of how small the payload turns out to be. Small responses then pay compression overhead for no benefit.

Third, if a CDN, load balancer or reverse proxy already negotiates encodings, running this middleware as well means the body is compressed at the origin and possibly re-encoded downstream. Pick one layer.

Finally, this is a response-body tool only. It does not compress request bodies, it does not manage caching, and it does not touch static file serving beyond whatever content types those routes set.

How expressjs/compression differs from a reverse proxy doing the gzip

The realistic alternative is not another npm package but moving the job out of the process entirely: let nginx, a CDN or a platform edge compress responses. The difference in approach is where the decision lives. A proxy compresses whatever bytes leave the origin, using its own content-type rules and its own configuration file, and it does so without touching application code. This middleware makes the decision inside the Node.js process, which means it can read request headers, run an arbitrary JavaScript filter, and expose res.flush() to application code.

That in-process position is the reason to choose it. If compression depends on application state, such as a per-tenant flag or a request header your users send, a proxy config cannot express that and a filter function can. The cost is that the compression work competes with your request handlers for the same event loop. The README exposes zlib and Brotli tuning knobs (level, memLevel, windowBits, chunkSize, strategy, brotli) precisely because that trade-off is real: the README states a higher level gives better compression but takes longer, while a lower level is much faster with less compression. A proxy moves that cost to a separate process.

Maintenance, releases and the MIT licence

The repository is not archived. The last push was on 2026-09-11, the same day as the v1.8.2 release, which followed v1.8.1 in July 2025 and v1.8.0 in February 2025. That is a slow but non-zero release cadence, consistent with a package whose surface is a single index.js and whose behaviour is bounded by Node's zlib API.

Upgrade cost is low in practice. The published files are LICENSE, README.md and index.js, and the dependency list is eight small packages (bytes, compressible, debug, destroy, negotiator, on-headers, safe-buffer, vary). Note the engines field says node >= 0.8.0, which is far below anything you should be running; the README's own Brotli note is the more useful floor, since it says Brotli is available in all currently supported Node.js LTS versions (v18+). If you are on an older runtime, gzip and deflate still work but br will not be offered.

The licence is MIT. That is permissive and imposes no copyleft obligation on your application, but this is a description of the licence identifier, not legal advice; read LICENSE and your own counsel's guidance before relying on it. The package.json also declares an Open Collective funding URL for the Express project, which is informational only.

Editorial conclusion

Adopt expressjs/compression if you run an Express or Connect-style server and your responses are mostly text, JSON or HTML that benefit from gzip or Brotli. Skip it if your payloads are already compressed images, video or archives, or if a CDN or reverse proxy in front of your app already negotiates encodings, since stacking both wastes CPU. Before rolling it out, verify what your own responses look like: whether the Content-Type values your routes set are recognised by the compressible module, whether your handlers set Content-Length so the 1kb threshold can be evaluated, and whether any route already sets Cache-Control: no-transform, which the middleware treats as an instruction not to compress. Check your Node.js version too, since the README notes Brotli requires a currently supported LTS line.

Frequently asked questions

What does expressjs/compression actually do?

It is Node.js compression middleware for Express-style servers. According to the README, it attempts to compress response bodies for all requests that traverse through the middleware, choosing among deflate, gzip and br (Brotli) based on the options and the request.

How do I install expressjs/compression?

It is published on the npm registry, and the README's install section gives the command npm install compression. You then require it and register it with app.use as shown in the README's Express example.

Which compression codings does expressjs/compression support?

The README lists deflate, gzip and br (brotli). It also notes that Brotli is available in all currently supported Node.js LTS versions (v18+), so older runtimes will not be offered br.

Why is my expressjs/compression response not compressed?

Several documented conditions suppress it. The default filter only considers responses whose Content-Type the compressible module recognises, responses below the 1kb threshold are skipped when the size is known, and the middleware never compresses responses carrying a Cache-Control header with the no-transform directive.

Official sources

  1. expressjs/compression on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/expressjs-compression.svg)](https://hysenlabs.com/projects/expressjs-compression)