Apache Static File Serving and Cache Control
This chapter covers efficient static file serving and proper browser cache control in Apache. Using mod_expires, mod_deflate, and ETag settings can dramatically improve web performance.
Apache Static File Serving Basics
Apache maps requested URLs to the filesystem by combining them with DocumentRoot.
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html
<Directory /var/www/html>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
mod_expires — Cache Expiry Headers
sudo a2enmod expires
sudo systemctl reload apache2
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 week"
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/json "access plus 0 seconds"
</IfModule>
mod_headers — Fine-Grained Cache-Control
sudo a2enmod headers
sudo systemctl reload apache2
<IfModule mod_headers.c>
# Hashed JS/CSS — 1 year strong cache (immutable)
<FilesMatch "\.[0-9a-f]{8,}\.(js|css)$">
Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>
<FilesMatch "\.(js|css)$">
Header set Cache-Control "max-age=31536000, public"
</FilesMatch>
<FilesMatch "\.(png|jpg|jpeg|gif|webp|ico|woff|woff2|ttf|svg)$">
Header set Cache-Control "max-age=2592000, public"
</FilesMatch>
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</FilesMatch>
<LocationMatch "^/api/">
Header set Cache-Control "no-cache, no-store, must-revalidate"
</LocationMatch>
</IfModule>
ETag Configuration
# Default ETag (includes inode — problematic in distributed servers)
FileETag MTime Size
# Recommended for distributed/load-balanced environments
FileETag MTime Size
# or disable completely:
FileETag None
Distributed environment warning: Default ETag includes inode numbers, which differ across servers. Use
FileETag MTime SizeorFileETag Noneto avoid cache mismatches.
mod_deflate — Gzip Compression
sudo a2enmod deflate
sudo systemctl reload apache2
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE image/svg+xml font/woff font/woff2
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|zip|gz|bz2)$ no-gzip dont-vary
Header append Vary Accept-Encoding
DeflateCompressionLevel 6
</IfModule>
Comprehensive Configuration Example
<VirtualHost *:443>
ServerName static.example.com
DocumentRoot /var/www/static
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/static.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/static.example.com/privkey.pem
<Directory /var/www/static>
Options -Indexes -MultiViews +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json image/svg+xml font/woff font/woff2
DeflateCompressionLevel 6
Header append Vary Accept-Encoding
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css)$">
Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>
<FilesMatch "\.html$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>
</VirtualHost>
Summary
| Optimization | Module | Effect |
|---|---|---|
| Cache expiry headers | mod_expires | Fewer requests from repeat visitors |
| Fine-grained Cache-Control | mod_headers | Immutable and precise cache policies |
| Gzip compression | mod_deflate | 50–80% reduction in text transfer size |
| ETag adjustment | FileETag | Prevents unnecessary revalidation in distributed environments |
| Disable directory listing | Options -Indexes | Security hardening |