Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Compress Responses with gzip and Brotli

By Kokil Thapa | Last reviewed: September 2026

Every uncached HTML page, JSON API payload, and CSS bundle you send over the wire costs time on slow mobile networks in Nepal and abroad. When you compress responses with gzip and Brotli, the browser receives fewer bytes and paints the page sooner. That is not a cosmetic tweak. It directly affects Core Web Vitals and technical SEO because transfer size feeds into LCP and TTFB. On production Laravel, WordPress, and Symfony stacks I maintain, compression is one of the cheapest wins you can ship in an afternoon.

Why should you compress responses with gzip and Brotli?

HTTP compression shrinks text-based assets before they leave your server. The client sends an Accept-Encoding header. Your server picks gzip, Brotli (br), or sends raw bytes when compression is off.

Text compresses well. HTML, CSS, JavaScript, JSON, SVG, and XML often drop 60–80% in size. Images and video are already compressed, so do not waste CPU on them.

On a legal-tech portal I built, enabling Brotli cut a 180 KB HTML document to about 28 KB. PageSpeed scores moved without touching application code. That pattern repeats across content-heavy Laravel sites and WooCommerce stores.

HTTP Compression Request FlowBrowserAccept-EncodingWeb ServerNginx / ApachePHP AppLaravel / WPCompression EngineBrotli preferred, gzip fallback, identity if unsupportedCompressed ResponseContent-Encoding: br or gzip
How browsers negotiate gzip and Brotli when you compress HTTP responses at the web server layer.

Compression sits at the edge, not inside PHP. Laravel 13 and Symfony 8.1 generate the same HTML whether or not Nginx compresses it. That separation keeps your app simple and your server configuration reusable across projects.

How does gzip compare to Brotli for HTTP compression?

Both are lossless. Brotli typically beats gzip by 15–25% on text at similar CPU cost on modern hardware. gzip has been standard since HTTP/1.1 and every client supports it. Brotli is safe for all current browsers but not for very old ones.

CriteriagzipBrotli
Compression ratio on HTML/CSS/JSGood (60–75% reduction)Better (70–85% reduction)
Browser supportUniversalAll modern browsers since ~2016
Server moduleBuilt into Nginx and ApacheRequires ngx_brotli or mod_brotli
PrecompressionCommon (.gz static files)Supported (.br static files)
CPU at high trafficLower per requestSlightly higher; cache helps
Best use caseFallback for all clientsPrimary for text assets

Run both. Serve Brotli when Accept-Encoding: br is present. Fall back to gzip. Never double-compress already compressed files like JPEG or WebP.

gzip vs Brotli ComparisongzipUniversal support60-75% size reductionBuilt-in on NginxBrotliModern browsers70-85% size reductionNeeds extra moduleRecommended StrategyServe Brotli first, gzip as fallbackSkip JPEG, PNG, WebP, MP4, WOFF2
gzip versus Brotli: compression ratio, support, and the dual-encoding strategy most production sites use.

How do you enable gzip and Brotli on Nginx?

Nginx ships with gzip. Brotli needs the ngx_brotli module compiled in or installed as a dynamic module on Ubuntu 22/24. After installation, add directives inside your server or http block.

gzip configuration

# /etc/nginx/nginx.conf or site conf
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml
  font/woff
  font/woff2;

gzip_vary on adds a Vary: Accept-Encoding header. Shared caches need that header to store separate variants. gzip_comp_level 5 balances CPU and ratio. Levels above 6 rarely help HTML.

Brotli configuration

brotli on;
brotli_comp_level 6;
brotli_min_length 256;
brotli_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml
  font/woff
  font/woff2;

Test the config and reload PHP-FPM's companion stack:

sudo nginx -t
sudo systemctl reload nginx

Verify with curl:

curl -H "Accept-Encoding: br" -I https://example.com/
curl -H "Accept-Encoding: gzip" -I https://example.com/

Look for Content-Encoding: br or Content-Encoding: gzip in the response headers. The official Nginx gzip module documentation lists every directive if you need tuning details.

On Laravel apps behind Nginx, compression applies to Blade HTML, Vite-built assets, and Sanctum JSON responses alike. No PHP change is required. That is the same pattern I use on sister sites sharing a Deployer 7 pipeline.

How do you enable gzip and Brotli on Apache?

Apache uses mod_deflate for gzip. Brotli needs mod_brotli, available in Apache 2.4 on Ubuntu when the module package is installed.

Enable gzip with mod_deflate

# Enable modules
sudo a2enmod deflate
sudo a2enmod headers

# VirtualHost or .htaccess
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/css
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/json application/xml
  AddOutputFilterByType DEFLATE image/svg+xml
  Header append Vary Accept-Encoding
</IfModule>

Enable Brotli with mod_brotli

sudo a2enmod brotli

<IfModule mod_brotli.c>
  AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css
  AddOutputFilterByType BROTLI_COMPRESS application/javascript
  AddOutputFilterByType BROTLI_COMPRESS application/json application/xml
  AddOutputFilterByType BROTLI_COMPRESS image/svg+xml
</IfModule>

Reload Apache after changes:

sudo apachectl configtest
sudo systemctl reload apache2

See the Apache mod_deflate reference for filter ordering and proxy edge cases. If you are migrating from Apache to Nginx, read our Apache to Nginx migration guide for a clean cutover.

Server Compression Setup Steps1. Install Brotli module (if needed)2. Add gzip + Brotli directives and MIME types3. Set min_length, comp_level, gzip_vary4. nginx -t or apachectl configtest5. Reload and verify with curl -H Accept-Encoding
Five-step pipeline to enable gzip and Brotli compression on production Nginx or Apache servers.

What MIME types and file sizes should you compress?

Compress text. Skip binary formats that are already compressed. A common mistake is listing image/jpeg or font/woff2 — WOFF2 is pre-compressed and JPEG gains nothing.

  • Always compress: text/html, text/css, application/javascript, application/json, application/xml, image/svg+xml
  • Usually skip: image/jpeg, image/png, image/webp, video/mp4, font/woff2
  • Minimum size: 256 bytes — smaller responses cost more CPU than they save bandwidth
  • API JSON: Large paginated payloads from REST APIs benefit heavily; see also Symfony serializer patterns for leaner payloads

For static assets built with Vite 8.x, precompress at build time. Generate .br and .gz files alongside your JS and CSS bundles. Nginx can serve them with gzip_static on and brotli_static on when the module supports it.

# Nginx static precompression
gzip_static on;
brotli_static on;

location ~* \.(js|css|svg)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

Build-time compression removes per-request CPU. On high-traffic WooCommerce shops, that difference matters during sale events.

How do you verify compression and avoid common production mistakes?

Verification belongs in every deploy checklist. I treat it like SSL checks — quick, repeatable, easy to automate.

  1. Open DevTools → Network → select a document → check Response Headers for Content-Encoding
  2. Run curl -H "Accept-Encoding: br,gzip" -sI https://yoursite.com/ and confirm encoding
  3. Compare transferred size vs content length in DevTools
  4. Run Lighthouse or PageSpeed Insights and confirm "Enable text compression" is green
  5. Check CDN settings — Cloudflare and similar services can compress at the edge and hide missing origin config

Common failures I see on client projects:

  • Double compression behind a CDN: Origin and CDN both compress, or neither does. Pick one layer.
  • Missing Vary header: Cached gzip response served to a client that cannot decode it.
  • Compressing images: Wastes CPU with zero byte savings.
  • Proxy buffering off: Some Nginx proxy setups skip compression for upstream PHP responses unless gzip_proxied is set.
  • HTTP/2 and HTTP/3: Compression still applies to response bodies; HPACK/QPACK header compression is separate.
Transfer Size Before vs After CompressionUncompressedHTML 180 KBCSS 95 KB + JS 220 KBBrotli EnabledHTML 28 KBCSS 18 KB + JS 62 KB-78%Faster TTFB and LCP on 3G networksTypical Laravel + Bootstrap page, production measurementsPair with caching and CDN for best resultsCompression + cache = fewer origin bytes overall
Typical transfer-size reduction when you compress responses with gzip and Brotli on a Laravel or WordPress page.

WordPress 7.1 and WooCommerce 11.1 sites on shared hosting often lack Brotli entirely. Moving to a VPS with Nginx and enabling both encodings is a standard step in our speed optimization service. Pair compression with Redis 8.10 object caching and proper browser cache headers for the full win.

PHP 8.5 and Laravel 13 do not need middleware for compression when the web server handles it. Avoid compressing inside PHP — you add latency and memory use for no benefit. Symfony 8.1 follows the same rule.

If you use a JSON formatter to inspect API output during development, remember that pretty-printed JSON compresses even better than minified JSON because repeated whitespace patterns compress well. Production APIs should still return compact JSON for clarity.

Key Takeaways

  • Enable both Brotli and gzip at the web server — Brotli first, gzip as universal fallback.
  • Compress text MIME types only; skip images, video, and WOFF2 fonts.
  • Set gzip_vary on and a min_length of 256 bytes to protect caches and CPU.
  • Precompress static Vite assets with .br and .gz files for zero per-request cost.
  • Verify with curl and DevTools after every deploy — do not assume CDN defaults are correct.
  • Combine compression with caching, CDN, and lean API payloads for maximum Core Web Vitals gain.

People Also Ask

Does gzip work with HTTP/2 and HTTP/3?

Yes. HTTP/2 and HTTP/3 compress response bodies with gzip or Brotli the same way HTTP/1.1 does. Header compression (HPACK or QPACK) is a separate mechanism. Enable body compression on your origin or CDN regardless of protocol version.

Should Laravel compress responses in middleware?

No. Let Nginx or Apache compress at the edge. PHP-level compression adds memory overhead and slows every request. Laravel 12 and 13 are designed to run behind a compressing reverse proxy.

Is Brotli worth it if gzip is already enabled?

Yes for text-heavy sites. Brotli saves an extra 15–25% over gzip on HTML and JavaScript. The module install is a one-time cost. CPU on modern servers is rarely the bottleneck for typical business traffic.

Can compression hurt SEO or Core Web Vitals?

Proper compression helps SEO by improving page speed signals. Google treats speed as a ranking factor. Smaller transfers improve LCP and TTFB. The only SEO risk is a misconfigured cache serving wrong encoding — fix that with the Vary: Accept-Encoding header.

Ship faster pages with gzip and Brotli today

When you compress responses with gzip and Brotli, you cut bandwidth, speed up pages on slow connections, and tick a major PageSpeed audit item — all without touching application code. The config takes minutes on Nginx or Apache. The gains last for every visitor.

If you want compression configured correctly alongside caching, CDN setup, and a full performance audit, see our testing and optimization service or browse the portfolio for live examples. For new builds, our web development team bakes compression into the server template from day one.

Contact us for a server audit, or read more on the blog about ongoing maintenance and production web systems built for Nepal and global clients.

Frequently Asked Questions

HTTP compression shrinks text-based assets before they leave your server. The browser sends Accept-Encoding; the server returns gzip, Brotli (br), or uncompressed bytes.

Every uncached HTML page, JSON API payload, and CSS bundle costs time on slow mobile networks. Compression cuts transfer size by 60 to 80 percent on text assets, which directly improves Core Web Vitals because transfer size feeds into LCP and TTFB. On a legal-tech portal I built, enabling Brotli cut a 180 KB HTML document to about 28 KB without touching application code. PageSpeed scores moved, and that pattern repeats on content-heavy Laravel sites and WooCommerce stores. It is one of the cheapest performance wins you can ship in an afternoon at the web server layer.

Both are lossless. Brotli typically beats gzip by 15 to 25 percent on HTML, CSS, and JavaScript at similar CPU cost on modern hardware. gzip has been standard since HTTP/1.1 and every client supports it. Brotli is safe for all current browsers but not for very old ones. gzip is built into Nginx and Apache; Brotli needs ngx_brotli or mod_brotli installed. Run both: serve Brotli when Accept-Encoding includes br, fall back to gzip for universal coverage. Never double-compress already compressed formats like JPEG or WebP.

Nginx ships with gzip built in. Enable gzip on, gzip_vary on, gzip_proxied any, gzip_comp_level 5, gzip_min_length 256, and list compressible MIME types in gzip_types. Brotli needs the ngx_brotli module on Ubuntu 22 or 24, then set brotli on, brotli_comp_level 6, brotli_min_length 256, and brotli_types. Test with sudo nginx -t and reload with sudo systemctl reload nginx. Verify using curl with Accept-Encoding br or gzip and check for Content-Encoding in response headers. On Laravel apps behind Nginx, compression applies to Blade HTML, Vite-built assets, and Sanctum JSON responses with no PHP change required.

Apache uses mod_deflate for gzip and mod_brotli for Brotli on Apache 2.4 with Ubuntu. Enable modules with a2enmod deflate, headers, and brotli. In your VirtualHost or .htaccess, use AddOutputFilterByType DEFLATE for text/html, text/css, application/javascript, application/json, application/xml, and image/svg+xml. Add Header append Vary Accept-Encoding for gzip. For Brotli, use AddOutputFilterByType BROTLI_COMPRESS on the same MIME types inside an IfModule mod_brotli block. Run sudo apachectl configtest, then sudo systemctl reload apache2. Check the Apache mod_deflate reference if you hit proxy or filter ordering edge cases during migration.

Compress text-based formats: text/html, text/css, application/javascript, application/json, application/xml, and image/svg+xml. A common mistake is listing image/jpeg or font/woff2 — WOFF2 is already pre-compressed and JPEG gains nothing. Usually skip image/jpeg, image/png, image/webp, video/mp4, and font/woff2. Large paginated REST API JSON payloads benefit heavily from compression. For static assets built with Vite 8.x, precompress at build time and generate .br and .gz files alongside JS and CSS bundles. Nginx can serve precompressed files with gzip_static on and brotli_static on when the module supports it.

No. Let Nginx or Apache compress at the edge. PHP-level compression adds memory overhead and slows every request.

Yes. HTTP/2 and HTTP/3 compress response bodies with gzip or Brotli the same way HTTP/1.1 does. Header compression with HPACK or QPACK is a separate mechanism.

Yes for text-heavy sites. Brotli saves an extra 15 to 25 percent over gzip on HTML and JavaScript. The ngx_brotli or mod_brotli install is a one-time cost. CPU on modern servers is rarely the bottleneck for typical business traffic. Serve Brotli when the client sends Accept-Encoding: br, and keep gzip as fallback for older clients. Pair this dual-encoding strategy with gzip_vary on so shared caches store separate variants. On high-traffic WooCommerce shops during sale events, build-time precompression of static assets removes per-request CPU entirely.

Open DevTools Network tab, select a document, and check Response Headers for Content-Encoding. Run curl with Accept-Encoding br,gzip against your URL and confirm the header. Compare transferred size versus content length in DevTools. Run Lighthouse or PageSpeed Insights and confirm Enable text compression is green. Also check CDN settings because Cloudflare and similar services can compress at the edge and hide missing origin configuration. I treat verification like SSL checks in every deploy checklist: quick, repeatable, and easy to automate after sudo nginx -t or apachectl configtest and a server reload.

Set gzip_min_length and brotli_min_length to 256 bytes. Smaller responses cost more CPU than they save in bandwidth, so compressing tiny payloads is wasteful on production Laravel, WordPress, and Symfony stacks. This threshold protects CPU on high-traffic API endpoints that return small JSON fragments while still compressing HTML documents, CSS bundles, and large paginated REST payloads where the 60 to 80 percent size reduction actually matters for LCP and TTFB on slow mobile networks in Nepal and abroad.

Proper compression helps SEO by improving page speed signals. Google treats speed as a ranking factor. Smaller transfers improve LCP and TTFB because transfer size feeds directly into those Core Web Vitals metrics. The only SEO risk is a misconfigured cache serving the wrong encoding to a client that cannot decode it. Fix that with the Vary Accept-Encoding header using gzip_vary on in Nginx or Header append Vary Accept-Encoding in Apache. Combine compression with Redis 8.10 object caching and proper browser cache headers for the full performance win on WordPress 7.1 and WooCommerce 11.1 sites.

No. Images and video are already compressed, so do not waste CPU on them. Skip image/jpeg, image/png, image/webp, and video/mp4 in your gzip_types and brotli_types lists. Compressing JPEG or WebP yields zero byte savings while adding server load on every request. The same rule applies to font/woff2, which is pre-compressed. Focus compression on text/html, text/css, application/javascript, application/json, application/xml, and image/svg+xml where lossless encoding typically drops payload size by 60 to 80 percent.

Double compression behind a CDN is frequent: origin and CDN both compress, or neither does, so pick one layer. Missing Vary Accept-Encoding causes cached gzip responses to be served to clients that cannot decode them. Compressing images wastes CPU with no savings. Some Nginx proxy setups skip compression for upstream PHP responses unless gzip_proxied is configured. WordPress 7.1 and WooCommerce 11.1 sites on shared hosting often lack Brotli entirely; moving to a VPS with Nginx and enabling both encodings is a standard speed optimization step. PHP 8.5 and Laravel 13 do not need middleware for compression.

For static assets built with Vite 8.x, generate .br and .gz files alongside your JS and CSS bundles at build time. On Nginx, enable gzip_static on and brotli_static on when the module supports it. Add long-cache headers on static locations for js, css, and svg files. Build-time compression removes per-request CPU cost entirely. On high-traffic WooCommerce shops, that difference matters during sale events when asset requests spike. The web server serves the precompressed file when present and falls back to on-the-fly gzip or Brotli for dynamic Blade HTML and API JSON from Laravel 13 or Symfony 8.1.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: