# nginx config for the yunkes frontend container. # # Two responsibilities: # 1. Serve the SPA's static assets (JS/CSS/images) from /usr/share/nginx/html. # 2. Reverse-proxy /api to the backend service so the SPA can call the # FastAPI app on the same origin (no CORS gymnastics in prod). # # The SPA fallback (try_files ... /index.html) lets vue-router own deep # links — a refresh on /customers hits index.html and is resolved client-side. server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # Gzip text-y responses — meaningful for the (sizeable) JS bundle. gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml; gzip_min_length 1024; # API reverse proxy. ``backend`` resolves via docker-compose's default # network; if the name changes, update here. Streaming endpoints # (/api/media/) work without buffering because we turn proxy_buffering # off — matches the dev-server behaviour (vite proxies /api/media first). # # DNS 加固(2026-09-04):nginx 默认启动时解析一次 hostname 并永久缓存, # backend 重建/IP 漂移后 502(两天两次实战)。改用 docker 内嵌 DNS # (127.0.0.11)按 TTL 重解析——variable proxy_pass 不带 URI,仍转发 # 原始完整请求路径(/api/...),与原行为一致。 location /api/ { resolver 127.0.0.11 valid=10s ipv6=off; set $yunke_backend http://backend:8000; proxy_pass $yunke_backend; 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_read_timeout 60s; # Allow websocket-style upgrades (harmless no-op for plain HTTP APIs # but cheap insurance against future ws use). proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Disable buffering so /api/media/{hash} streams straight through. proxy_buffering off; } # Static SPA assets — long cache (immutable content hash in filename). location ~* \.(?:js|css|woff2?|png|jpe?g|gif|svg|ico)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # SPA fallback: any path that isn't a real file resolves to index.html # so vue-router can pick it up. location / { try_files $uri $uri/ /index.html; } }