diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..29af0b4 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,70 @@ +user nginx; +worker_processes auto; + +error_log /dev/stderr warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /dev/stdout; + + sendfile on; + tcp_nopush on; + keepalive_timeout 65; + server_tokens off; + + # Compression: takes the main JS bundle from ~1.9 MB down to ~530 kB on the wire + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 1024; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/javascript + application/json + application/xml + image/svg+xml + font/woff + font/woff2; + + server { + # 80 is the conventional port; 3000 is what Dokploy targets by default in + # its domain config. Binding both means Traefik reaches the app whichever + # one the dashboard's Port field is set to, so a deploy can't 502 purely + # because those two numbers disagree. + listen 80; + listen 3000; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Vite writes content-hashed filenames here, so they can be cached forever. + # Only add_header is used — combining it with `expires` emits two + # conflicting Cache-Control headers. + location /assets/ { + add_header Cache-Control "public, max-age=31536000, immutable"; + access_log off; + } + + # index.html must be revalidated every time or deploys won't take effect + location = /index.html { + add_header Cache-Control "no-cache"; + } + + location / { + # This line forces Nginx to pass routing back to React Router + try_files $uri $uri/ /index.html; + } + } +}