Request
Could the default self-hosted +server build gain end-to-end precompression support?
For a Node self-hosted build, the useful outcome is:
vike build emits Brotli (and optionally gzip) variants for eligible files in dist/client/.
- The generated production server prefers those variants instead of dynamically encoding the same asset on every request.
This likely needs coordination with Universal Deploy rather than being only a Vike build hook, but Vike is where the user-facing contract should live: Vike creates the client artifact and selects/configures the default production adapter.
This is production-motivated
We reached this while investigating real high-CPU fallout in a Vike production deployment. A fresh current Vike self-hosted install resolves @universal-deploy/node@0.1.9 with srvx@0.11.22, whose default static path dynamically runs Brotli quality 11 for every eligible response. The immediate dependency fix is tracked in universal-deploy#34.
Upgrading to srvx 0.12 is an important safety fix because it moves request-time Brotli to quality 4. But q4 on every cache miss is still repeated work, and quality 4 also produces a larger representation than a build can afford to create once.
On Node 24.14.0, for a representative 126,324-byte minified JavaScript asset:
| Brotli quality |
encode time |
output size |
| 4 |
2.7 ms |
45,117 bytes |
| 11 |
157.6 ms |
39,489 bytes |
The point is not to run q11 on a request. It is to run it once at build time, then serve those 39,489 bytes with no compression CPU on the production request path and with a known Content-Length.
Current end-to-end gap
This means merely installing a Vite compression plugin is not an end-to-end workaround: it can create the files, but the default Vike/Universal Deploy server doesn't probe or select them. Conversely, enabling encodings: true in the server would add a missing stat on every request until the build actually ships variants.
Suggested shape
The exact configuration boundary is open, but the implementation should ideally:
- Target the client output only (
dist/client/), not the server bundle.
- Precompress compressible text assets above a sensible threshold; skip already-compressed formats and omit a variant when it doesn't reduce size.
- Use maximum-quality Brotli at build time, with bounded concurrency so a large build doesn't create its own CPU/memory spike. Gzip can be emitted as a compatibility fallback.
- Enable
srvx's precompressed-variant lookup for the generated Node server and prefer disk variants over on-the-fly compression.
- Decide explicitly whether q4 remains a fallback for files without variants or whether a fully precompressed build uses
compress: false.
- Preserve correct
Vary, coding-specific validators, Content-Length, conditional request, and range behavior.
- Be target-aware: managed platforms/CDNs often handle compression themselves, so this may be a self-hosted Node default or an opt-in rather than an unconditional cost for every deployment target.
- Cover public-directory files as well as Vite-emitted hashed assets, while keeping configuration available for very large/unusual assets.
An integration test could build a fresh +server app, request a hashed JS asset with Accept-Encoding: br, and assert that the adjacent .br file is served with the correct headers and without creating a request-time Brotli stream.
Related runtime fallbacks
Build-time precompression is the preferred path for Vike-owned static assets. It doesn't replace runtime fallbacks for files that weren't part of the build or for stable dynamic responses:
srvx#289 tracks a bounded encoded-representation cache for the static server's on-the-fly fallback.
universal-middleware#353 tracks an opt-in representation cache for responses using @universal-middleware/compress.
Those paths are complementary, but precompression is the only one that moves both fast and maximum-quality encoding completely out of production requests for Vike's immutable build assets.
Request
Could the default self-hosted
+serverbuild gain end-to-end precompression support?For a Node self-hosted build, the useful outcome is:
vike buildemits Brotli (and optionally gzip) variants for eligible files indist/client/.This likely needs coordination with Universal Deploy rather than being only a Vike build hook, but Vike is where the user-facing contract should live: Vike creates the client artifact and selects/configures the default production adapter.
This is production-motivated
We reached this while investigating real high-CPU fallout in a Vike production deployment. A fresh current Vike self-hosted install resolves
@universal-deploy/node@0.1.9withsrvx@0.11.22, whose default static path dynamically runs Brotli quality 11 for every eligible response. The immediate dependency fix is tracked in universal-deploy#34.Upgrading to
srvx0.12 is an important safety fix because it moves request-time Brotli to quality 4. But q4 on every cache miss is still repeated work, and quality 4 also produces a larger representation than a build can afford to create once.On Node 24.14.0, for a representative 126,324-byte minified JavaScript asset:
The point is not to run q11 on a request. It is to run it once at build time, then serve those 39,489 bytes with no compression CPU on the production request path and with a known
Content-Length.Current end-to-end gap
vike buildputs all browser assets indist/client/, but it doesn't emit.br/.gzrepresentations.createStatic({ dir: staticDir }).srvx0.12 can serve adjacent precompressed variants, butencodingsis intentionally off by default, while on-the-flycompressdefaults to true.prod.statictype is onlyboolean | string, so an app cannot ask the generated server to enableencodings, disable the on-the-fly fallback, or pass the other static options.This means merely installing a Vite compression plugin is not an end-to-end workaround: it can create the files, but the default Vike/Universal Deploy server doesn't probe or select them. Conversely, enabling
encodings: truein the server would add a missingstaton every request until the build actually ships variants.Suggested shape
The exact configuration boundary is open, but the implementation should ideally:
dist/client/), not the server bundle.srvx's precompressed-variant lookup for the generated Node server and prefer disk variants over on-the-fly compression.compress: false.Vary, coding-specific validators,Content-Length, conditional request, and range behavior.An integration test could build a fresh
+serverapp, request a hashed JS asset withAccept-Encoding: br, and assert that the adjacent.brfile is served with the correct headers and without creating a request-time Brotli stream.Related runtime fallbacks
Build-time precompression is the preferred path for Vike-owned static assets. It doesn't replace runtime fallbacks for files that weren't part of the build or for stable dynamic responses:
srvx#289tracks a bounded encoded-representation cache for the static server's on-the-fly fallback.universal-middleware#353tracks an opt-in representation cache for responses using@universal-middleware/compress.Those paths are complementary, but precompression is the only one that moves both fast and maximum-quality encoding completely out of production requests for Vike's immutable build assets.