EponwebPractical guides to web development and technology
Performance Optimization

Gzip vs. Brotli: Why Compression Alone Won't Save a Bloated Bundle

Switching to Brotli offers diminishing returns if your JavaScript bundle is full of dead code and unnecessary whitespace.

Mariana Costa
Mariana CostaPrincipal Backend Architect6 min read
Editorial image illustrating Gzip vs. Brotli: Why Compression Alone Won't Save a Bloated Bundle

I still remember the Monday morning in March when a lead developer stormed into my office, tablet in hand, showing me a Lighthouse score of 38. "We enabled Brotli on the CDN last week," he said, frustration leaking into his voice. "Why is the Time to Interactive still garbage?" The team had fallen for the most convenient lie in backend optimization: the belief that flipping a switch on a compression algorithm absolves you of writing efficient code.

The problem wasn't the server configuration. The problem was a 2.3MB unminified JavaScript payload containing three different date-picking libraries and an entire PDF SDK that was loaded on the homepage just in case a user clicked a "Download Brochure" button three scrolls down. Compression algorithms are not magic wands; they are mathematical trash compactors. If you feed a compactor a refrigerator, it just crushes the refrigerator; it doesn't make it light.

Photographic detail related to Gzip vs. Brotli: Why Compression Alone Won't Save a Bloated Bundle

The "Good Enough" Fallacy of Gzip

There is a pervasive sentiment in 2026 that Gzip is legacy tech—a relic of the early web that we should have abandoned years ago. While I agree that Brotli generally offers superior compression ratios, treating Gzip as insufficient is a misunderstanding of where performance bottlenecks actually live.

I audited a SaaS dashboard last month where the engineering team was obsessing over Gzip levels. They spent weeks tuning their Nginx configuration, moving from level 4 to level 6, squeezing out an extra 3% compression ratio. Meanwhile, their main.js file was still exporting an unused Redux store and an entire Lodash library for a single _.isEmpty utility call.

When we benchmarked that specific file, the results were damning. The raw, unminified bundle was 850KB. Gzip (level 6) brought it down to 210KB. Brotli (level 4) pushed it to 190KB. That 20KB saving is negligible on a fiber connection and imperceptible on 4G. However, when we actually removed the dead code via tree-shaking and ran the minifier, the raw file dropped to 120KB. Gzip compressed that to 38KB, and Brotli to 32KB.

The takeaway is brutal: minification provided an 85% reduction in payload size. Switching from Gzip to Brotli provided a 15% reduction of the already minimized file. Focusing on the algorithm without addressing the source material is rearranging deck chairs on the Titanic. You cannot compress your way out of architectural bloat.

Brotli Levels and the CPU Trade-off

The narrative gets more complicated when we discuss the backend resources required to achieve these gains. Brotli is not free. At Eponweb, we ran extensive load tests on our Kubernetes clusters last year to determine the viability of Brotli level 11 for dynamic HTML content.

What we found was a classic resource trap. While the static assets served from the CDN were pre-compressed and fine, the dynamic API responses were being compressed on the fly. Moving from Gzip-6 to Brotli-4 resulted in a 15% increase in CPU utilization on our Node.js workers. Moving to Brotli-11 for that extra 5% compression efficiency spiked CPU usage by nearly 60%. At peak traffic, this caused request queuing, effectively negating any latency benefits gained from the smaller packet size.

There is a specific scenario I recall involving a checkout service where we implemented Brotli-11 dynamically. The checkout page loaded 200ms faster, but the server response times for the "Place Order" API endpoint degraded because the server threads were busy compressing JSON responses. We had to roll back immediately.

This highlights a critical principle of backend architecture: disaster recovery and performance monitoring must go hand in hand. If you are going to implement aggressive compression, you must have a circuit breaker or a fallback strategy. For instance, we now configure our load balancers to serve pre-compressed static Brotli files, but we strictly limit dynamic compression to Gzip-4 or Brotli-1 to prevent CPU starvation. The rollback strategy involves simple feature flags in the reverse proxy configuration that disable Brotli instantly if metrics alert on CPU load.

Why Tree-Shaking Beats Compression Algorithms Every Time

Developers often confuse "optimization" with "configuration." Real optimization happens at the code level. It is painful to admit, but many of us are lazy with our imports. In the modern ecosystem, using tree-shaking is non-negotiable, yet I still see import { map } from 'rxjs' in production codebases.

If you send a 1MB file to the client, you are asking their browser to parse, compile, and execute 1MB of JavaScript. Even if Brotli squeezes it down to 250KB for the transfer, the browser still has to deal with the 1MB of logic once it hits memory. The V8 engine doesn't get a break just because the download was zipped. The cost of parsing and execution is often the dominant factor in Time to Interactive, not the network transfer time.

Consider a legacy Angular project we modernized recently. The team was debating between Gzip and Brotli, but the real culprit was the lack of code splitting. The entire application, including the admin dashboard and the marketing landing page, was contained in a single bundle. By splitting the routes and lazy loading the admin module, we reduced the initial payload by 60%. We didn't touch the compression settings, yet the site felt significantly faster.

This ties into the broader issue of bandwidth economy. As discussed in our guide to responsive images, efficiency is about sending only what is necessary, when it is necessary. You wouldn't send a 4K image to a mobile device just because you have a good zip algorithm; don't send an entire admin library to a user who just wants to read the homepage.

The Safety Net: Pre-compression and Rollback Strategies

From a backend architecture perspective, relying on on-the-fly compression for your critical assets is a smell in 2026. It introduces latency (the time it takes to compress the file) and unpredictability.

The robust approach is to treat compression as a build step, not a runtime step. Your CI/CD pipeline should generate three versions of every asset: raw, Gzip, and Brotli. The web server then serves the pre-compressed file using the Content-Encoding header. This shifts the computational cost to build time, where it doesn't affect the user experience.

However, this introduces a deployment complexity. If your build script fails to generate the .br files, or if you deploy a configuration that expects .br files that aren't there, you return 404 errors to your users. This is where the principle of least privilege and defense-in-depth applies. Your Nginx or Apache configuration must include a try_files directive that gracefully falls back to Gzip or the raw file if the Brotli version is missing.

Last quarter, we had a regression where a dependency update in our Webpack configuration corrupted the Brotli output. Because our server config was set up to check for the Brotli file and fail over silently to Gzip if it was absent, 99.9% of users experienced zero downtime. If we had relied on dynamic compression, the corrupted build would have gone unnoticed until the server started throwing 500 errors while trying to compress the output.

Compression Is the Floor, Not the Ceiling

We need to stop treating compression as a performance strategy. It is a standard of hygiene. You absolutely must use it, and yes, Brotli is generally better than Gzip for static assets. But if your performance roadmap consists solely of "enable Brotli," you have failed before you even started.

The battle for speed is won in the build process, in the removal of unused dependencies, and in the architectural discipline of code splitting. We saw a drastic reduction in Time to Interactive simply by chopping up a monolithic bundle, regardless of how it was zipped.

Don't let the perfect be the enemy of the good, but don't let the "good" be the enemy of the necessary. Minify first, tree-shake second, and only then should you worry about whether Brotli is saving you that extra 20 kilobytes. If you skip the first two steps, the third one is just polishing a sink that is already overflowing.

Go check your bundle analyzer today. I guarantee you will find more savings there than in any nginx.conf tweak.

Read next