# Cannamanage — Nginx reverse proxy configuration # Location: /etc/nginx/sites-available/cannamanage.conf # Symlink: ln -s /etc/nginx/sites-available/cannamanage.conf /etc/nginx/sites-enabled/ # Rate limiting zone limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s; limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/s; # HTTPS server server { listen 443 ssl http2; server_name cannamanage.plate-software.de; ssl_certificate /etc/letsencrypt/live/cannamanage.plate-software.de/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/cannamanage.plate-software.de/privkey.pem; 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; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; # Security headers add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; 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 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' js.stripe.com; frame-src js.stripe.com hooks.stripe.com; img-src 'self' data: blob: *.stripe.com; style-src 'self' 'unsafe-inline'; connect-src 'self' api.stripe.com; font-src 'self' data:;" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # Request body size (for file uploads if needed) client_max_body_size 10m; # Frontend (Next.js) location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; 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_cache_bypass $http_upgrade; } # Backend API location /api/v1/ { limit_req zone=api burst=40 nodelay; proxy_pass http://127.0.0.1:8080/api/v1/; 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_read_timeout 60s; } # Auth endpoints — stricter rate limit location /api/v1/auth/ { limit_req zone=auth burst=10 nodelay; proxy_pass http://127.0.0.1:8080/api/v1/auth/; 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; } # Stripe webhooks — no rate limit, Stripe handles its own retry logic location /api/v1/webhooks/stripe { proxy_pass http://127.0.0.1:8080/api/v1/webhooks/stripe; 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 Stripe-Signature $http_stripe_signature; } # Health check endpoint (internal only) location /health { proxy_pass http://127.0.0.1:8080/actuator/health; access_log off; allow 127.0.0.1; deny all; } # Block common exploit paths location ~ /\.(git|env|htaccess) { deny all; return 404; } } # HTTP → HTTPS redirect server { listen 80; server_name cannamanage.plate-software.de; # Allow ACME challenge for cert renewal location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } }