Jekyll plugin that generates Brotli (.br), Zstandard (.zst) and
gzip (.gz) siblings for text assets at the end of a build. A
precompressed-capable static server then serves the best encoding the
client accepts — with zero per-request compression CPU.
Pairs with Caddy's file_server { precompressed gzip br zstd } (see
caddy_config_file_local_hosting).
Static sites are always "pre"-compressed anyway — the compression
simply happens at build time instead of request time.
The plugin shells out to the platform compressor CLIs — install them on the build host (not the web server; visitors need nothing):
| Tool | Generates | Minimum | Tested baseline |
|---|---|---|---|
brotli |
.br |
1.1.0 | 1.2.1 (macOS) |
zstd |
.zst |
1.5.5 | 1.5.7 (macOS) |
gzip |
.gz |
any | preinstalled on macOS/Linux; ships with Git for Windows |
The minimums match Ubuntu 24.04 LTS — the oldest versions exercised by
CI on every push; the build checks tool --version once per format and
aborts on tool X found — need >= Y (or warns with
fail_on_error: false). gzip has no version check: Apple and GNU
gzip use incomparable version strings and the flags used are
POSIX-stable.
# macOS (Homebrew)
brew install brotli zstd
# Debian / Ubuntu / GitHub Actions
sudo apt-get install -y brotli zstd
# Fedora
sudo dnf install brotli zstd
# Alpine
apk add brotli zstd
# Windows — scoop (both in main bucket)
scoop install brotli zstd
# or winget for brotli:
winget install -e --id Google.Brotli
# gzip.exe ships with Git for Windows / MSYS2Windows note: package availability shifts often (upstream release artifacts have changed layout without notice, breaking scoop/winget manifests). If a package manager fails you, grab the official static binaries directly from the projects' GitHub releases — google/brotli and facebook/zstd — or build under WSL, where the Linux instructions apply and
gzipis built in.
Missing tools abort the build with a clear message
(fail_on_error: false downgrades to a warning) — see Configuration.
A host that can't install a tool can exclude its format instead:
formats: [gz].
Add to the site's Gemfile:
group :jekyll_plugins do
gem "jekyll-compress-flow", "~> 0.1"
# for local development against a checkout:
# gem "jekyll-compress-flow", path: "../jekyll-compress-flow"
endInstall the compressor binaries on the build host:
brew install brotli zstd gzip # macOS
apt-get install brotli zstd gzip # Debian/Ubuntu CIThat's it — JEKYLL_ENV=production bundle exec jekyll build writes
file.br, file.zst and file.gz next to every text asset.
The plugin only writes the siblings — the web server must be
configured to negotiate them via Accept-Encoding:
- Caddy: built in —
file_server { precompressed gzip br zstd }. Caddy prefers the best encoding the client accepts and falls back to the plain file. (docs) - nginx:
gzip_static on;serves.gztransparently (docs). Brotli needs thengx_brotlimodule withbrotli_static on;(docs) — not in stock nginx builds. There is no stock Zstandard module yet. - Apache:
mod_brotli/mod_deflatecompress on the fly; serving precompressed files needsOptions MultiViews+AddEncoding br .br/AddEncoding x-gzip .gz/AddEncoding zstd .zstplusAddTypemappings in the docroot's.htaccess. On-the-flymod_brotliis usually the simpler choice (docs).
compress_flow:
enabled: true # default: Jekyll.env == "production"
priority: 10 # integer 0..10; default 10, after fingerprinting
formats: [br, zst, gz] # subset of the three; unknown names abort
extensions: [html, css, js, json, xml, svg, txt, map]
min_size: 0 # bytes — skip tiny files
fail_on_error: true # false: warn + skip instead of failing buildThe post-write dispatcher accepts integer priorities from 0 through 10 so site-specific compression remains after the default Fingerprint Flow range (11–19).
- Runs on
:site, :post_writeat low priority (10), after normal-priority post-processors and the priority-12 Fingerprint Flow pass — works with any generator (pages, posts, feeds, sitemap, search JSON). - Compressed siblings are never re-compressed (extensions not in the list); images/fonts/PDFs are skipped (already compressed formats).
- A missing CLI tool aborts the build once per format
(
fail_on_error: falsedowngrades to a warning) — never silently ships uncompressed output. -fflags make output idempotent — reruns overwrite stale siblings.
bundle install
bundle exec rake # rubocop + markdownlint + bundler-audit + rspec + gem build
bundle exec rake quick # rubocop + rspec onlyThis project is licensed under the GNU Affero General Public License v3.0 or
later (AGPL-3.0-or-later). See LICENSE.