Files
landing/nginx.conf
mifi c963e34766
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
Proper 410/404 pages
2026-02-16 01:18:38 -03:00

150 lines
4.6 KiB
Nginx Configuration File

# Minimal nginx configuration for static site delivery
# Security headers are handled upstream by Traefik
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Performance optimizations
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# Gzip compression for text-based assets
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
# text/html is always gzipped by default; listing it again causes "duplicate MIME type" warning
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/rss+xml
application/atom+xml
image/svg+xml;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# HTML files: no-cache (always revalidate)
location ~ \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
expires 0;
}
# CSS and JavaScript: long cache with immutable
location ~* \.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# Images: long cache (30 days)
location ~* \.(jpg|jpeg|png|gif|webp|avif)$ {
add_header Cache-Control "public, max-age=2592000";
access_log off;
}
# SVG images: long cache (30 days)
location ~* \.svg$ {
add_header Cache-Control "public, max-age=2592000";
add_header Content-Type image/svg+xml;
access_log off;
}
# Fonts: long cache with immutable
location ~* \.(woff|woff2|ttf|otf|eot)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# Documents: medium cache (30 days)
location ~* \.(pdf|doc|docx)$ {
add_header Cache-Control "public, max-age=2592000";
access_log off;
}
# robots.txt: short cache (1 day)
location = /robots.txt {
add_header Cache-Control "public, max-age=86400";
access_log off;
}
# favicon: long cache (30 days)
location = /favicon.svg {
add_header Cache-Control "public, max-age=2592000";
add_header Content-Type image/svg+xml;
access_log off;
}
# Default location
location / {
try_files $uri $uri/ /index.html;
}
# 410 Gone: permanently removed URLs (tells crawlers to deindex)
error_page 410 /410.html;
location = /410.html {
add_header Cache-Control "no-cache, must-revalidate";
add_header Content-Type "text/html; charset=utf-8";
}
location = /2024/02/18/hello-world/ { return 410; }
location = /2024/02/18/hello-world { return 410; }
location = /pt/ { return 410; }
location = /pt { return 410; }
location = /feed/ { return 410; }
location = /feed { return 410; }
location = /category/uncategorized/feed/ { return 410; }
location = /category/uncategorized/feed { return 410; }
location = /category/uncategorized/ { return 410; }
location = /category/uncategorized { return 410; }
location = /comments/feed/ { return 410; }
location = /comments/feed { return 410; }
# Allow .well-known (security.txt, ACME challenge, etc.)
location ^~ /.well-known/ {
add_header Cache-Control "public, max-age=86400";
}
# Deny access to other hidden files (.git, .env, etc.)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Custom 404 page (for missing static assets; SPA routes still try index.html first via try_files)
error_page 404 /404.html;
location = /404.html {
add_header Cache-Control "no-cache, must-revalidate";
add_header Content-Type "text/html; charset=utf-8";
}
}
}