When using Apache's mod_deflate for compression, you might notice that CSS and JavaScript files containing query parameters (e.g., aggregator.css?version=1.2
or jquery.js?cache=clear
) aren't being gzipped, while their parameter-less counterparts are properly compressed. This occurs because of how FilesMatch
pattern matching works with URLs containing query strings.
The standard configuration:
<IfModule mod_deflate.c>
<FilesMatch "\.(css|js|x?html?|php)$">
SetOutputFilter DEFLATE
</FilesMatch>
</IfModule>
only matches against the filename portion of the URI, not the query string. The ?
and everything after it in a URL is considered part of the query string, which means files with parameters fall outside the matching pattern.
Solution 1: Using LocationMatch
Replace FilesMatch
with LocationMatch
which examines the entire request URI:
<IfModule mod_deflate.c>
<LocationMatch "\.(css|js|x?html?|php)(\?.*)?$">
SetOutputFilter DEFLATE
</LocationMatch>
</IfModule>
The pattern (\?.*)?$
makes the query string optional while still capturing the entire URI.
Solution 2: Separate Rules for Parameterized Files
For more precise control, create separate matching rules:
<IfModule mod_deflate.c>
# Standard files
<FilesMatch "\.(css|js|x?html?|php)$">
SetOutputFilter DEFLATE
</FilesMatch>
# Files with parameters
<LocationMatch "\.(css|js)(\?.*)?$">
SetOutputFilter DEFLATE
</LocationMatch>
</IfModule>
- Always test changes using curl:
curl -I -H "Accept-Encoding: gzip" "http://yoursite.com/file.js?param=1"
- Consider adding these headers for better caching:
Header append Vary: Accept-Encoding Header append Vary: User-Agent
- For dynamic files (like PHP), ensure compression isn't being handled at both Apache and application levels
While enabling compression for parameterized files improves performance, be aware that:
- Each unique URL (including different parameters) creates a separate cache entry
- Highly dynamic parameters (like session IDs) should be excluded from compression rules
- Monitor memory usage when enabling compression for many unique URLs
Enable mod_deflate logging by adding:
DeflateFilterNote Input instream
DeflateFilterNote Output outstream
DeflateFilterNote Ratio ratio
LogFormat "\"%r\" %{outstream}n/%{instream}n (%{ratio}n%%)" deflate
CustomLog logs/deflate_log deflate
This helps identify which files are being compressed and their compression ratios.
When examining YSlow or Chrome DevTools, you might notice certain CSS/JS resources aren't being gzipped despite having mod_deflate properly configured. This typically occurs with URLs containing query parameters like:
https://example.com/aggregator.css?ver=1.0
https://example.com/misc/jquery.js?cache=12345
The default
Modify your .htaccess configuration with this improved pattern:
SetOutputFilter DEFLATE
The key improvement is (\?.*)?$
which makes the query string optional in the pattern matching.
Using cURL:
curl -I -H "Accept-Encoding: gzip,deflate" "https://yoursite.com/script.js?param=1"
Look for Content-Encoding: gzip
in the response headers.
Browser DevTools:
Check the Network tab and examine the Content-Encoding header for affected resources.
For maximum compatibility, you can force compression for all text-based content regardless of extension:
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css text/javascript application/javascript
AddOutputFilterByType DEFLATE application/json application/xml
When dealing with parameterized assets, ensure your caching headers are properly configured to avoid duplicate compression:
FileETag None
Header unset ETag
Header set Cache-Control "max-age=2592000, public"