nginx
Nginx Static File Serving Configuration
Optimized Nginx config for serving static files with caching headers, directory structure, and SPA fallback support.
Overview
Nginx excels at serving static files directly from disk with minimal overhead. This configuration includes aggressive caching for hashed assets, proper MIME types, SPA (single-page application) fallback routing, and security hardening.
Configuration
# /etc/nginx/sites-available/static-site.conf
server {
listen 80;
server_name static.example.com;
# Root directory for static files
root /var/www/html;
index index.html;
# Character encoding
charset utf-8;
# Disable access log for static assets (reduce I/O)
access_log off;
error_log /var/log/nginx/static_error.log warn;
# Security: hide Nginx version
server_tokens off;
# Hashed assets — cache aggressively (JS, CSS, images with hash in filename)
location ~* \.(js|css)$ {
expires 1y; # Cache for 1 year
add_header Cache-Control "public, immutable"; # Never revalidate
add_header X-Content-Type-Options "nosniff";
try_files $uri =404;
}
# Images and fonts — cache for 30 days
location ~* \.(png|jpg|jpeg|gif|svg|ico|webp|avif|woff2?|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public";
try_files $uri =404;
}
# Media files — cache for 7 days
location ~* \.(mp4|webm|ogg|mp3|wav)$ {
expires 7d;
add_header Cache-Control "public";
try_files $uri =404;
}
# HTML files — no caching (always fetch latest)
location ~* \.html$ {
expires -1; # No cache
add_header Cache-Control "no-store, no-cache, must-revalidate";
try_files $uri =404;
}
# SPA fallback — serve index.html for all unmatched routes
location / {
try_files $uri $uri/ /index.html; # Check file, then directory, then fallback
}
# Block access to hidden files (.env, .git, etc.)
location ~ /\. {
deny all;
return 404;
}
# Block access to source maps in production
location ~* \.map$ {
deny all;
return 404;
}
# Custom 404 page
error_page 404 /404.html;
location = /404.html {
internal; # Only accessible via error_page
}
}
Key Options Explained
try_files $uri $uri/ /index.html— First checks if the exact file exists, then the directory, then falls back toindex.html. Essential for SPAs with client-side routing.expires 1y+immutable— Tells browsers to cache the file for one year and never revalidate. Use only for files with content hashes in their filenames (e.g.,app.a1b2c3.js).expires -1— Sends headers that instruct browsers to always revalidate HTML files, ensuring users get the latest version.access_log off— Disabling access logs for static assets reduces disk I/O and improves performance under heavy traffic.internal— The error page location can only be reached through internal Nginx redirects, not direct client requests.
Common Modifications
- Remove SPA fallback: For a plain static site, change
try_filestotry_files $uri $uri/ =404;to return 404 for missing files. - Enable directory listing: Add
autoindex on;inside a location block to allow browsing directories. - Serve pre-compressed files: Add
gzip_static on;so Nginx serves.gzfiles if they exist alongside originals. - Add CORS headers: Add
add_header Access-Control-Allow-Origin "*";for assets served to other domains. - Custom MIME types: Include
include /etc/nginx/mime.types;and add entries for formats like.wasmor.avif.