server {
listen 80;
server_name example.com;
root /var/www/html;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on; # nginx ≥ 1.25.1; en versiones anteriores: listen 443 ssl http2;
server_name example.com;
root /var/www/html;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Security headers (no rompen una SPA; una CSP completa depende de tu app)
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'; object-src 'none'; base-uri 'self'" always;
add_header Strict-Transport-Security "max-age=31536000" always; # añade includeSubDomains cuando TODOS los subdominios vayan por https
# Nunca servir ficheros ocultos: .git/, .env, .htpasswd… (salvo .well-known)
location ~ /\.(?!well-known) {
deny all;
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Cache static assets — «immutable» solo es correcto si el nombre lleva hash (main-3F2A1B.js)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# Repetidas: un add_header aquí anula las del server
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'; object-src 'none'; base-uri 'self'" always;
add_header Strict-Transport-Security "max-age=31536000" always; # añade includeSubDomains cuando TODOS los subdominios vayan por https
}
location / {
try_files $uri $uri/ /index.html;
}
}