# Cashumints.space API - Nginx Configuration # # Installation: # sudo cp deploy/nginx.conf /etc/nginx/sites-available/cashumints-api # sudo ln -s /etc/nginx/sites-available/cashumints-api /etc/nginx/sites-enabled/ # sudo nginx -t # sudo systemctl reload nginx # # SSL Certificate (Let's Encrypt): # sudo certbot --nginx -d api.cashumints.space # Rate limiting zones limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=admin_limit:10m rate=5r/s; limit_conn_zone $binary_remote_addr zone=conn_limit:10m; # Upstream API server upstream cashumints_api { server 127.0.0.1:3000; keepalive 32; } # Redirect HTTP to HTTPS server { listen 80; listen [::]:80; server_name api.cashumints.space; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$server_name$request_uri; } } # Main HTTPS server server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name api.cashumints.space; # SSL Configuration ssl_certificate /etc/letsencrypt/live/api.cashumints.space/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.cashumints.space/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # Modern SSL configuration ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS add_header Strict-Transport-Security "max-age=63072000" always; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Logging access_log /var/log/nginx/cashumints-api.access.log; error_log /var/log/nginx/cashumints-api.error.log; # Connection limits limit_conn conn_limit 20; # Gzip compression gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types application/json application/javascript text/plain text/css text/xml; # Client settings client_max_body_size 1m; client_body_timeout 10s; client_header_timeout 10s; # Proxy settings proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ""; proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; # Health check (no rate limit) location = /v1/health { proxy_pass http://cashumints_api; proxy_cache_bypass 1; } # Swagger documentation (relaxed rate limit) location /docs { limit_req zone=api_limit burst=10 nodelay; proxy_pass http://cashumints_api; } location = /openapi.json { limit_req zone=api_limit burst=10 nodelay; proxy_pass http://cashumints_api; # Cache OpenAPI spec proxy_cache_valid 200 5m; } # Admin endpoints (strict rate limit) location /v1/admin { limit_req zone=admin_limit burst=5 nodelay; proxy_pass http://cashumints_api; # Only allow from specific IPs (optional) # allow 10.0.0.0/8; # allow 192.168.0.0/16; # deny all; } # API endpoints location /v1 { limit_req zone=api_limit burst=50 nodelay; proxy_pass http://cashumints_api; # Cache GET requests for 10 seconds proxy_cache_valid 200 10s; } # Root and favicon location = / { proxy_pass http://cashumints_api; } location = /favicon.ico { return 204; access_log off; } # Block common attack vectors location ~ /\. { deny all; access_log off; log_not_found off; } location ~* \.(git|env|sql|bak|old|tmp)$ { deny all; access_log off; log_not_found off; } }