Last updated: April 2026 · AnomixLabs Tech Team
Static file caching is the lowest-cost optimization that reduces server costs, increases page speed, and improves user experience — if configured correctly.
Why Cache Static Files?
In a typical web page, 60-80% of network traffic consists of static files (CSS, JS, images, fonts). Caching these files:
- Reduces page load time by 70-90% on repeat visits
- Decreases server bandwidth usage
- Increases Lighthouse Performance score
- Improves Core Web Vitals (especially LCP)
Cache-Control Headers
Basic Nginx static file caching configuration:
# /etc/nginx/sites-available/anomix.conf
server {
listen 443 ssl http2;
server_name anomixlabs.com;
# Static files — long cache
location /static/ {
alias /var/www/anomix/staticfiles/;
expires 1y;
add_header Cache-Control 'public, max-age=31536000, immutable';
add_header Vary Accept-Encoding;
access_log off;
gzip_static on;
}
# Media files — short cache
location /media/ {
alias /var/www/anomix/media/;
expires 30d;
add_header Cache-Control 'public, max-age=2592000';
access_log off;
}
}
The immutable Directive: Powerful Optimization
The Cache-Control: immutable directive tells the browser, 'this file will never change, don't even ask during refresh.' This eliminates unnecessary conditional requests (304 Not Modified) during soft reloads (F5).
Condition: Immutable should only be used with file names containing a content hash. With Django:
# settings.py
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# This setting adds hashes to filenames during collectstatic:
# main.css → main.3a7f9b2c.css
# This way, when the content changes, the URL changes, and the cache is invalidated seamlessly
Brotli vs Gzip: Which is Better?
Brotli (Google, 2015) offers a better compression ratio than gzip — but at a higher CPU cost:
- Text files (HTML/CSS/JS): Brotli compresses 15-25% better than gzip
- For static files: Pre-compressed .br file — no CPU cost
- For dynamic content: gzip is more practical (faster encoding)
- Browser support: As of 2026, Brotli has 97%+ browser support
# Nginx Brotli module (nginx-module-brotli)
brotli on;
brotli_comp_level 6; # Between 1-11, 6 is a good balance
brotli_static on; # Pre-compressed .br files
brotli_types text/plain text/css application/javascript
application/json image/svg+xml;
# Gzip fallback
gzip on;
gzip_static on;
gzip_vary on;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
Nginx with HTTP/3 (QUIC)
HTTP/3 overcomes some limitations of HTTP/2 by using the UDP-based QUIC protocol — especially noticeable performance improvements on networks with packet loss:
# HTTP/3 support with Nginx 1.25+
server {
listen 443 ssl;
listen 443 quic reuseport; # QUIC for HTTP/3
http2 on;
ssl_certificate /etc/letsencrypt/live/site.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site.com/privkey.pem;
# Inform the browser about HTTP/3 support
add_header Alt-Svc 'h3=":443"; ma=86400';
}
Note: Nginx mainline (1.25+) or Nginx Plus is required for HTTP/3. It's available in the Ubuntu 22.04+ package repository. HTTP/3 is automatically enabled behind a Cloudflare CDN.
Nginx map Directive for WEBP and AVIF
Serve modern image formats based on browser support:
# Select modern format based on browser Accept header
map $http_accept $img_suffix {
default '';
'~*avif' '.avif';
'~*webp' '.webp';
}
server {
location ~* \.(jpe?g|png)$ {
add_header Vary Accept;
try_files $uri$img_suffix $uri =404;
}
}
With this configuration, browsers supporting AVIF will receive .avif, those supporting WebP will receive .webp, and others will get the original JPEG/PNG. To add AVIF/WebP generation to the Django collectstatic pipeline, django-imagekit or Pillow can be used.
Django collectstatic and CI/CD Integration
collectstatic should be automated in the production deployment process:
# .github/workflows/deploy.yml
- name: Collect static files
run: |
python manage.py collectstatic --noinput
# ManifestStaticFilesStorage generates hashes in this step
- name: Deploy to server
run: |
rsync -avz staticfiles/ user@server:/var/www/app/staticfiles/
# No Nginx restart needed — files changed, so URLs did too
WhiteNoise alternative: For small to medium-sized projects, django-whitenoise serves static files from the Python process without Nginx. Compression and caching are automatic. It eliminates Nginx configuration complexity.
# settings.py — WhiteNoise
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware', # Immediately after SecurityMiddleware
...
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# This setting handles both hashed filenames and Brotli/gzip compression
Nginx Cache Integration with CDN
CDNs like Cloudflare, AWS CloudFront, or BunnyCDN sit in front of Nginx:
- Origin Pull (Lazy Caching): The CDN fetches from the origin on the first request, then serves from the CDN. The most common mode.
- Push (Proactive Caching): Files are pushed to the CDN during deployment. Full control with CI/CD.
- Cache invalidation: Clear the CDN cache after a new deployment — Zone Purge in Cloudflare, Create Invalidation in CloudFront.
# Cloudflare cache purge (add to deploy script)
curl -X POST 'https://api.cloudflare.com/client/v4/zones/{ZONE_ID}/purge_cache' \
-H 'Authorization: Bearer {API_TOKEN}' \
-H 'Content-Type: application/json' \
--data '{"purge_everything": true}'
Lighthouse Cache Audit
The Lighthouse warning 'Serve static assets with an efficient cache policy' lists static files with a cache duration shorter than 1 year. Fix: For all files under /static/, use max-age=31536000 (1 year) + immutable.
If you are using ManifestStaticFilesStorage for cache busting, you can completely bypass this Lighthouse warning.
Summary
The basic recipe for Nginx static caching: hashed filenames with ManifestStaticFilesStorage + Cache-Control max-age=31536000 + immutable. An additional 15-25% size saving with Brotli static compression. HTTP/3 can be configured in modern Nginx. Adding a CDN provides global distribution and reduced origin load as a bonus.
Frequently Questions
What happens if CDN and Nginx cache conflict? expand_more
Should I cache media files too? expand_more
How do I integrate collectstatic into CI/CD? expand_more
What is a cache invalidation strategy? expand_more
Is WhiteNoise sufficient or is Nginx static serving mandatory? expand_more
What do gzip_static and brotli_static do in Nginx? expand_more
How much performance improvement does enabling HTTP/3 provide? expand_more
Ali Kasımoğlu
Full-stack Developer & Founder of AnomixLabs
A software developer specializing in the Python and Django ecosystem. Focuses on modern web architectures, AI integrations, and minimalist user experiences. Under the AnomixLabs umbrella, he aims to transform complex problems into lean and effective digital solutions.