Skip to content
Back to Blog
Performance9 min read

.htaccess Gzip Compression and Browser Caching Configuration

Complete .htaccess snippets for Apache mod_deflate and mod_expires that reduce bandwidth and improve page load times. Includes configuration examples and optimization techniques.

Written by Abdul AbrorTechnical Hosting Support Engineer
.htaccess Gzip Compression and Browser Caching Configuration
On this page

Slow page loads drive visitors away and hurt search rankings. Two of the most effective performance optimizations you can implement at the Apache level are gzip compression and browser caching, both controlled through your .htaccess file. These techniques reduce bandwidth consumption and server load while dramatically improving user experience.

Why Gzip Compression and Browser Caching Matter

Every time a browser requests your site, the server sends HTML, CSS, JavaScript, and other assets. Without optimization, these files transfer at their full size and get re-downloaded on every visit.

Gzip compression reduces file sizes before transmission. Text-based assets like HTML, CSS, and JavaScript often compress by 60-80%, meaning a 100 KB JavaScript file might transfer as only 20-30 KB. Smaller transfers mean faster page loads and lower bandwidth costs.

Browser caching tells visitors' browsers to store copies of static assets locally. When someone returns to your site or navigates to another page, their browser reuses the cached files instead of downloading them again. This reduces server requests and speeds up subsequent page loads.

Both optimizations work at the HTTP protocol level through response headers. Apache's mod_deflate handles compression, while mod_expires and mod_headers control caching directives.

Prerequisites and Module Verification

Before implementing these configurations, verify that the required Apache modules are enabled. Most shared hosting environments on cPanel have these modules active by default, but VPS and dedicated server administrators should confirm.

Required Apache Modules

For compression: - mod_deflate (compression) - mod_filter (processing pipeline)

For caching: - mod_expires (expiration headers) - mod_headers (HTTP headers)

On a VPS or dedicated server, check module status:

apachectl -M | grep deflate
apachectl -M | grep expires
apachectl -M | grep headers
apachectl -M | grep filter

If modules are missing, enable them:

sudo a2enmod deflate
sudo a2enmod expires
sudo a2enmod headers
sudo a2enmod filter
sudo systemctl restart apache2

On cPanel servers, modules are typically managed through EasyApache. Shared hosting users should contact their provider if modules are unavailable.

Implementing Gzip Compression with mod_deflate

The mod_deflate module compresses responses on the fly. The configuration specifies which MIME types to compress and handles browser compatibility.

Complete mod_deflate Configuration

Add this block to your .htaccess file in the document root:

<IfModule mod_deflate.c>
  # Compress HTML, CSS, JavaScript, Text, XML and fonts
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-otf
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/otf
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml

  # Remove browser bugs (old browsers)
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
  Header append Vary User-Agent
</IfModule>

What This Configuration Does

AddOutputFilterByType DEFLATE: Applies gzip compression to specific MIME types. The configuration covers common web assets including HTML, CSS, JavaScript, JSON, XML, SVG, and web fonts.

BrowserMatch directives: Handle compatibility with very old browsers that had broken gzip implementations. Modern browsers all support compression properly, but these rules prevent issues if old user agents appear in your logs.

Header append Vary User-Agent: Tells caching proxies that the response varies based on the User-Agent header. This prevents compressed responses from being served to clients that don't support compression.

What NOT to Compress

Do not compress already-compressed formats: - Images (JPEG, PNG, WebP, GIF) - Videos (MP4, WebM) - Compressed archives (ZIP, RAR, 7z) - PDFs (usually already compressed)

Compressing these formats wastes CPU cycles and may even increase file size slightly. The configuration above deliberately omits these MIME types.

Implementing Browser Caching with mod_expires

Browser caching reduces repeat requests by telling browsers how long to store files locally. The configuration sets expiration times based on file type.

Complete mod_expires Configuration

Add this block to your .htaccess file:

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 1 month"

  # HTML and data
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
  ExpiresByType application/xml "access plus 0 seconds"
  ExpiresByType text/xml "access plus 0 seconds"

  # Feed
  ExpiresByType application/rss+xml "access plus 1 hour"
  ExpiresByType application/atom+xml "access plus 1 hour"

  # Favicon
  ExpiresByType image/x-icon "access plus 1 year"

  # Media: images, video, audio
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType video/ogg "access plus 1 year"
  ExpiresByType audio/ogg "access plus 1 year"
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/webm "access plus 1 year"

  # CSS and JavaScript
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/javascript "access plus 1 year"

  # Fonts
  ExpiresByType application/x-font-ttf "access plus 1 year"
  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType application/x-font-woff "access plus 1 year"
  ExpiresByType application/font-woff "access plus 1 year"
  ExpiresByType application/font-woff2 "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
</IfModule>

Cache Duration Strategy

HTML (0 seconds): HTML files should not be cached because they often contain dynamic content or references to updated assets. Setting expiration to zero forces browsers to revalidate on every visit.

Static assets (1 year): CSS, JavaScript, images, fonts, and other static resources get long expiration times. When you update these files, use versioned filenames or query strings to bust the cache.

Feeds (1 hour): RSS and Atom feeds change periodically but not on every page load. A one-hour cache balances freshness with reduced server load.

Adding Cache-Control Headers

For more control, supplement mod_expires with explicit Cache-Control headers:

<IfModule mod_headers.c>
  # Cache static assets for 1 year
  <FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|css|js|woff|woff2|ttf|otf)$">
    Header set Cache-Control "max-age=31536000, public"
  </FilesMatch>

  # Don't cache HTML
  <FilesMatch "\.(html|htm|php)$">
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires 0
  </FilesMatch>
</IfModule>

The Cache-Control header provides finer control than Expires. The public directive allows shared caches (CDNs, proxies) to store the response, while max-age sets the cache duration in seconds.

Complete Combined Configuration

Here's a production-ready .htaccess snippet combining both optimizations:

# Gzip Compression
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/otf
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/xml

  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
  Header append Vary User-Agent
</IfModule>

# Browser Caching
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 1 month"

  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
  ExpiresByType application/xml "access plus 0 seconds"
  ExpiresByType text/xml "access plus 0 seconds"

  ExpiresByType application/rss+xml "access plus 1 hour"
  ExpiresByType application/atom+xml "access plus 1 hour"

  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType video/mp4 "access plus 1 year"
  ExpiresByType video/webm "access plus 1 year"

  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/javascript "access plus 1 year"

  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType application/x-font-woff "access plus 1 year"
  ExpiresByType application/font-woff2 "access plus 1 year"
</IfModule>

# Cache-Control Headers
<IfModule mod_headers.c>
  <FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|css|js|woff|woff2|ttf|otf)$">
    Header set Cache-Control "max-age=31536000, public"
  </FilesMatch>

  <FilesMatch "\.(html|htm|php)$">
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires 0
  </FilesMatch>
</IfModule>

Verification and Testing

Check Compression Headers

Verify gzip compression is working:

curl -H "Accept-Encoding: gzip,deflate" -I https://yourdomain.com/style.css

Look for these response headers:

Content-Encoding: gzip
Vary: Accept-Encoding

Check Caching Headers

Verify cache headers are present:

curl -I https://yourdomain.com/style.css

Look for:

Cache-Control: max-age=31536000, public
Expires: [future date]

Browser Developer Tools

Use Chrome or Firefox DevTools:

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Reload the page
  4. Click on any asset (CSS, JS, image)
  5. Check Response Headers for Content-Encoding: gzip and Cache-Control
  6. Check the Size column – it should show both transferred size (compressed) and actual size

Online Testing Tools

PageSpeed tools will report on compression and caching: - Google PageSpeed Insights - GTmetrix - WebPageTest - Pingdom Tools

These tools identify uncompressed resources and assets without cache headers. Run a test before and after implementing the configuration to measure improvement.

Common Issues and Troubleshooting

Compression Not Working

500 Internal Server Error: The .htaccess syntax is invalid or required modules are not enabled. Check Apache error logs:

tail -f /var/log/apache2/error.log

Headers not appearing: Verify mod_deflate is loaded. On shared hosting, contact your provider. On VPS/dedicated servers, enable the module and restart Apache.

Some file types not compressed: Check MIME type configuration. Your server must recognize the file extension and map it to the correct MIME type in mime.types.

Caching Issues

Cache not updating: If you change a CSS or JS file but visitors see the old version, the browser cache hasn't expired. Solutions: - Use versioned filenames: style.v2.css instead of style.css - Add query strings: style.css?v=2 - Reduce cache time for files you update frequently

Dynamic content cached: If HTML pages show stale content, your cache headers are too aggressive. HTML should have Cache-Control: no-cache or very short expiration.

.htaccess Not Processing

If your .htaccess file is ignored: - Verify AllowOverride All is set in your Apache virtual host configuration - Check file permissions (should be readable by the web server) - Ensure the file is named exactly .htaccess with no extra extension

Performance Impact and Monitoring

While exact results vary by site, typical improvements from these optimizations include:

Gzip compression: Text-based assets usually compress by 60-80%. A site serving 2 MB of uncompressed CSS and JavaScript might reduce that to 400-500 KB compressed.

Browser caching: Returning visitors load cached assets from disk instead of downloading them. This eliminates HTTP requests entirely for cached resources, dramatically improving load time for repeat visitors.

Bandwidth reduction: Compression and caching together can reduce bandwidth consumption by 70-90% for sites with returning visitors.

Monitor these metrics: - Total page size and transferred size in DevTools - Time to First Byte (TTFB) - Largest Contentful Paint (LCP) - Server bandwidth usage in cPanel or your server monitoring tool

WordPress-Specific Considerations

WordPress sites can use these .htaccess rules without issues. However:

Caching plugins: Many WordPress caching plugins (W3 Total Cache, WP Super Cache, WP Rocket) add their own .htaccess rules. If you use a caching plugin, it may already handle these optimizations. Check the plugin's settings before adding manual rules to avoid conflicts.

Plugin and theme updates: When WordPress updates plugins or themes, their CSS and JavaScript URLs typically change, automatically busting browser caches. This makes long cache times safe.

Page caching vs browser caching: These .htaccess rules control browser caching of static assets. Page caching (generating static HTML) is separate and typically handled by plugins.

Conclusion

Gzip compression and browser caching are foundational performance optimizations that every Apache-hosted site should implement. The .htaccess configurations above compress text-based assets and cache static resources, reducing bandwidth consumption and improving page load times for visitors.

These optimizations work at the HTTP protocol level and complement other performance techniques like image optimization, minification, and CDN usage. After implementing these rules, verify them using browser developer tools and online testing services, and monitor the impact on your site's performance metrics and bandwidth usage.

For shared hosting users, these rules typically work immediately. VPS and dedicated server administrators should verify module availability and adjust configurations to match their specific server setup and traffic patterns.

FAQ

Does gzip compression slow down my server?

Compression uses CPU resources but the trade-off is almost always positive. Modern servers compress quickly, and the reduction in bandwidth and transfer time outweighs the compression overhead. High-traffic sites may want to monitor CPU usage when implementing compression.

Can I use these rules on nginx?

nginx uses different configuration syntax. For nginx, enable gzip in your server block configuration using gzip on; and related directives. The concepts are identical but the syntax differs completely.

What if I need to update a cached file?

Use cache-busting techniques: change the filename, append a version query string, or temporarily reduce the cache time. Most build tools and bundlers support automatic cache-busting through content hashes in filenames.

Do these rules work with CDNs?

Yes. CDNs typically respect your origin server's caching headers. However, CDNs often provide their own compression and caching controls that may override these settings. Configure CDN settings to complement your origin rules.

Should I cache different content types differently?

Yes. The configuration above uses different cache times for different content types. Static assets that rarely change (fonts, images) get long cache times, while dynamic content (HTML) gets shorter times or no caching.