When implementing gzip_static on
in Nginx configurations, many developers encounter a puzzling requirement: the uncompressed version of a file must exist alongside its .gz
counterpart, even when you explicitly want to serve only pre-compressed content.
Nginx's standard behavior with gzip_static on
requires both file versions because:
- It performs client capability checking (Accept-Encoding headers)
- Needs fallback for clients that don't support gzip
- Validates file existence before serving compressed content
Here's a typical working configuration:
location /assets/ {
gzip_static on;
gunzip on;
try_files $uri =404;
}
Despite documentation suggesting gzip_static always
should work without uncompressed files, in practice it often doesn't. This is because:
location /static/ {
gzip_static always; # Should work without uncompressed files
gunzip on; # Required for decompression when needed
try_files $uri.gz =404; # Alternative approach
}
If you absolutely need to serve only .gz
files:
Option 1: Modify try_files directive
location ~* \.(css|js)$ {
try_files $uri.gz @nogzip;
gzip_static on;
}
location @nogzip {
# Handle cases where .gz doesn't exist
}
Option 2: Use a map directive
map $uri $gzip_uri {
default $uri.gz;
}
server {
location / {
try_files $gzip_uri $uri @fallback;
}
}
While maintaining both compressed and uncompressed versions consumes disk space, the benefits include:
- Automatic client capability handling
- Simplified deployment pipelines
- Better compatibility with CDN edge cases
When troubleshooting:
# Check file permissions:
ls -la /path/to/assets/
# Verify Nginx can see both files:
sudo -u www-data ls /path/to/assets/
# Test with curl:
curl -I -H "Accept-Encoding: gzip" http://yoursite.com/assets/file.css
curl -I http://yoursite.com/assets/file.css
When configuring Nginx's gzip_static
module (particularly in version 1.4.4), many developers encounter a puzzling requirement: the server insists on having both compressed (.gz) and uncompressed versions of static files available, even when we clearly want to serve only pre-compressed assets.
Here's a typical working setup where both file versions exist:
http {
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
server {
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
try_files $uri =404;
}
}
}
When testing with these files:
/assets/app-abc123.css # Dummy content
/assets/app-abc123.css.gz # Real compressed CSS
Nginx correctly serves the compressed version when gzip_static on
. However, removing the uncompressed version causes 404 errors, contrary to what some might expect from the documentation.
The gzip_static
module actually performs these checks in order:
- Checks if client accepts gzip (Accept-Encoding header)
- Verifies existence of .gz version
- Also verifies existence of original file
For cases where you truly want only .gz files:
location ^~ /assets/ {
gzip_static always; # Bypass client capability check
gunzip on; # For clients that don't support gzip
try_files $uri.gz =404; # Explicitly look for .gz files
}
In real-world deployments, maintaining both versions offers these advantages:
- Fallback for non-gzip clients
- Easier debugging without compression
- Consistent behavior with other static files
For modern frontend builds, consider this pattern:
# During deployment:
find /assets/ -name '*.gz' | while read f; do
orig=${f%.gz}
touch "$orig" # Create empty original file
chmod 644 "$orig"
done