Two-stage build (node:22-slim builder + nginxinc/nginx-unprivileged:alpine runtime) with SPA fallback routing, long-lived cache headers for fingerprinted assets, non-root user (UID 101), and no Node.js toolchain in runtime image (82 MB vs 329 MB+ single-stage). Verified by ui/tests/build/verify_production_image.sh covering build, health, SPA routing, non-root, stdout logging, cache-control headers, SIGTERM exit 0, Node.js absent, secret-free layers, and dep-layer cache hit. 102 integration tests still pass; shellcheck clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 lines
544 B
Nginx Configuration File
23 lines
544 B
Nginx Configuration File
server {
|
|
listen 8080;
|
|
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
|
|
# SPA fallback: all unmatched paths → app shell
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
|
|
# Long-lived cache for fingerprinted assets
|
|
location ~* \.(js|css|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|ico)$ {
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Never cache the entry point
|
|
location = /index.html {
|
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
|
}
|
|
}
|