The Full Configuration
Here's everything together:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mysite.com;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mysite.com/chain.pem;
root /var/www/mysite.com/public_html;
index index.html;
access_log /var/log/nginx/mysite.access.log;
error_log /var/log/nginx/mysite.error.log;
server_tokens off;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'; "
"img-src 'self' https://*; "
"style-src 'self' 'unsafe-inline'; "
"script-src 'self'; "
"font-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'; "
"form-action 'self';" always;
# Pre-compressed assets
gzip_static on;
# Let's Encrypt
location ~ /.well-known/acme-challenge {
root /usr/share/nginx/html;
allow all;
}
# Clean URLs — drop .html extension
location / {
if ($request_method !~ ^(GET|HEAD)$) {
return 405;
}
try_files $uri $uri.html $uri/ =404;
}
# Hashed assets — immutable
location ~* \.[a-f0-9]{8,}\.(js|css|png|jpg|jpeg|gif|ico|webp|svg|woff2|woff)$ {
expires max;
add_header Cache-Control "public, immutable";
}
# Non-hashed static — cache with revalidation
location ~* \.(png|jpg|jpeg|gif|ico|webp|woff2|woff|ttf|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
location ~* \.(css|js|xml|txt)$ {
expires 7d;
add_header Cache-Control "public, must-revalidate";
}
# Housekeeping
location = /favicon.ico {
access_log off;
log_not_found off;
try_files $uri =204;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ ~$ {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(yml|yaml|env|bak|swp|dist|config)$ {
deny all;
access_log off;
log_not_found off;
}
# Custom error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
No PHP. No database. No CMS updates. Just Nginx doing what it's always been best at — serving files fast, securely, and without drama.
Technical Audit Summary
This guide is maintained as part of a modular, SSL-first framework. Each configuration is audited for production stability and modern security standards.
Last Audit: May 2026
Nginx: 1.30.0
Compatibility: Tested against current stable releases. While optimized for the stack above, core logic remains relevant for Nginx 1.26+.