Skip to content

feat(server): add compression support for production server#8595

Open
jack-old-archive wants to merge 3 commits into
web-infra-dev:mainfrom
jack-old-archive:feat/server-compression
Open

feat(server): add compression support for production server#8595
jack-old-archive wants to merge 3 commits into
web-infra-dev:mainfrom
jack-old-archive:feat/server-compression

Conversation

@jack-old-archive

Copy link
Copy Markdown
Contributor

Summary

Add gzip/br/deflate compression middleware for the production server via server.compression config option.

This allows developers to enable response compression without relying on a reverse proxy like nginx.

Usage:

// modern.config.ts

export default defineConfig({

  server: {

    compression: true, // gzip by default

    // or: compression: { encoding: 'br' }

  },

});


## Related Links

Closes #6920

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->

- [ ] I have added changeset via `pnpm run change`.
- [ ] I have updated the documentation.
- [ x] I have added tests to cover my changes.

- Fix "unkown" -> "unknown" in streaming error message
- Fix "preResovled" -> "preResolved" in comment
- Fix "createRemixReuqest" -> "createRemixRequest" function name
The generated modern-app-env.d.ts is missing the @rsbuild/core/types
reference, causing TypeScript errors for import.meta.env.DEV and
other Rsbuild built-in environment variables.

Closes web-infra-dev#8522
@changeset-bot

changeset-bot Bot commented May 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 16769ee

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@netlify

netlify Bot commented May 7, 2026

Copy link
Copy Markdown

Deploy Preview for modernjs-byted ready!

Name Link
🔨 Latest commit 16769ee
🔍 Latest deploy log https://app.netlify.com/projects/modernjs-byted/deploys/69fccac6e11dc80008b84c45
😎 Deploy Preview https://deploy-preview-8595--modernjs-byted.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 99 (🟢 up 2 from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@keepview

keepview commented May 25, 2026

Copy link
Copy Markdown
Contributor

@Jack-sh1 Thanks for the PR — the motivation (compression without nginx/CDN) is valid. 👍

That said, I don't think this needs to live in the framework: Modern.js already exposes Hono middleware via the custom server (server/modern.server.ts), so users can enable hono/compress in a few lines:

// server/modern.server.ts
import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime';
import { compress } from 'hono/compress';

export default defineServerConfig({
  middlewares: [
    { name: 'compress', order: 'pre', handler: compress() as MiddlewareHandler },
  ],
});

I verified this with modern serve: responses come back with content-encoding: gzip. This is basically what the plugin does internally, so server.compression is mostly a config alias over something already achievable.

A couple of issues with the current implementation, FWIW:

  • brotli is broken — hono/compress uses the web-standard CompressionStream (gzip/deflate only); new CompressionStream('br') throws at runtime, so encoding: 'br' will crash.
  • forcing encoding bypasses Accept-Encoding negotiation — Hono only negotiates when encoding is left unset.

Suggestion: document the recipe above instead of adding a new config/plugin. If you do want it built in, basing it on http-compression (already used by the dev server — supports brotli + a filter) would keep dev/prod consistent and make brotli actually work. Thanks again!

@jack-old-archive

Copy link
Copy Markdown
Contributor Author

@Jack-sh1 Thanks for the PR — the motivation (compression without nginx/CDN) is valid. 👍

That said, I don't think this needs to live in the framework: Modern.js already exposes Hono middleware via the custom server (server/modern.server.ts), so users can enable hono/compress in a few lines:

// server/modern.server.ts
import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime';
import { compress } from 'hono/compress';

export default defineServerConfig({
  middlewares: [
    { name: 'compress', order: 'pre', handler: compress() as MiddlewareHandler },
  ],
});

I verified this with modern serve: responses come back with content-encoding: gzip. This is basically what the plugin does internally, so server.compression is mostly a config alias over something already achievable.

A couple of issues with the current implementation, FWIW:

  • brotli is broken — hono/compress uses the web-standard CompressionStream (gzip/deflate only); new CompressionStream('br') throws at runtime, so encoding: 'br' will crash.
  • forcing encoding bypasses Accept-Encoding negotiation — Hono only negotiates when encoding is left unset.

Suggestion: document the recipe above instead of adding a new config/plugin. If you do want it built in, basing it on http-compression (already used by the dev server — supports brotli + a filter) would keep dev/prod consistent and make brotli actually work. Thanks again!

@keepview Thanks for the thorough review!

You're right about both issues — the brotli crash and the Accept-Encoding bypass are real bugs that need fixing. I'll address them.

That said, I'd still like to keep this as a built-in feature for a couple reasons:

  1. Not everyone uses a custom server — many users just want compression out of the box without writing server/modern.server.ts. A one-line config (server.compression: true) is significantly lower friction.
  2. Dev/prod consistency — the dev server already uses http-compression for compression. Having the same behavior in prod (via the same library) means users don't need to reason about two different compression paths.
  3. Common need — issue [Feature]: Gzip compression for production files #6920 has been open for a while, which suggests this is a recurring request.

I'll refactor this PR to:

  • Replace hono/compress with http-compression (already used by the dev server)
  • Fix brotli support (http-compression handles it natively)
  • Properly negotiate Accept-Encoding headers
  • Keep dev/prod behavior consistent

Would you be open to reviewing the updated implementation once it's ready?

@keepview

Copy link
Copy Markdown
Contributor

@Jack-sh1 Thanks for the PR — the motivation (compression without nginx/CDN) is valid. 👍
That said, I don't think this needs to live in the framework: Modern.js already exposes Hono middleware via the custom server (server/modern.server.ts), so users can enable hono/compress in a few lines:

// server/modern.server.ts
import { defineServerConfig, type MiddlewareHandler } from '@modern-js/server-runtime';
import { compress } from 'hono/compress';

export default defineServerConfig({
  middlewares: [
    { name: 'compress', order: 'pre', handler: compress() as MiddlewareHandler },
  ],
});

I verified this with modern serve: responses come back with content-encoding: gzip. This is basically what the plugin does internally, so server.compression is mostly a config alias over something already achievable.
A couple of issues with the current implementation, FWIW:

  • brotli is broken — hono/compress uses the web-standard CompressionStream (gzip/deflate only); new CompressionStream('br') throws at runtime, so encoding: 'br' will crash.
  • forcing encoding bypasses Accept-Encoding negotiation — Hono only negotiates when encoding is left unset.

Suggestion: document the recipe above instead of adding a new config/plugin. If you do want it built in, basing it on http-compression (already used by the dev server — supports brotli + a filter) would keep dev/prod consistent and make brotli actually work. Thanks again!

@keepview Thanks for the thorough review!

You're right about both issues — the brotli crash and the Accept-Encoding bypass are real bugs that need fixing. I'll address them.

That said, I'd still like to keep this as a built-in feature for a couple reasons:

  1. Not everyone uses a custom server — many users just want compression out of the box without writing server/modern.server.ts. A one-line config (server.compression: true) is significantly lower friction.
  2. Dev/prod consistency — the dev server already uses http-compression for compression. Having the same behavior in prod (via the same library) means users don't need to reason about two different compression paths.
  3. Common need — issue [Feature]: Gzip compression for production files #6920 has been open for a while, which suggests this is a recurring request.

I'll refactor this PR to:

  • Replace hono/compress with http-compression (already used by the dev server)
  • Fix brotli support (http-compression handles it natively)
  • Properly negotiate Accept-Encoding headers
  • Keep dev/prod behavior consistent

Would you be open to reviewing the updated implementation once it's ready?

sure~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants