# blut24-landing — nginx front end on 192.168.12.81 # # Nuxt/Nitro listens on 127.0.0.1:3002 (PM2 cluster, 2 workers). nginx terminates # TLS and proxies. Because PM2 reloads workers one at a time, in-flight requests # are drained rather than dropped during a deploy. # # TLS: run certbot once the DNS for blut24.com points here — # certbot --nginx -d blut24.com -d www.blut24.com # It rewrites the :443 block below in place. # # --------------------------------------------------------------------------- # THIS FILE IS A REFERENCE COPY, NOT THE LIVE CONFIG. # --------------------------------------------------------------------------- # The live host runs ISPConfig, which GENERATES # /etc/nginx/sites-available/blut24.com.vhost from a template plus the # per-site "nginx Directives" field (dbispconfig → web_domain.nginx_directives, # domain_id 4). Editing the .vhost by hand is wiped on the next regeneration. # # So the location blocks below are kept in sync with that DB column by hand. # When you change one, change the other — see deploy/README.md, section # "nginx: where the config actually lives". upstream blut24_landing { server 127.0.0.1:3002; keepalive 32; } server { listen 80; listen [::]:80; server_name blut24.com www.blut24.com; # certbot writes its challenge here; everything else goes to HTTPS once TLS # is in place. Until then, this block serves the site directly. location /.well-known/acme-challenge/ { root /var/www/html; } location / { proxy_pass http://blut24_landing; 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 ""; # A deploy retires a worker while it may still be finishing a request. # Retry the next upstream instead of surfacing a 502 to the visitor. proxy_next_upstream error timeout http_502 http_503 http_504; proxy_next_upstream_tries 3; proxy_connect_timeout 5s; proxy_read_timeout 60s; } # Hashed build assets are immutable — cache them hard. location /_nuxt/ { proxy_pass http://blut24_landing; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Connection ""; add_header Cache-Control "public, max-age=31536000, immutable"; } # --- /downloads/ : real files on disk, NOT proxied to Nuxt ---------------- # # Everything else on this vhost goes to Node, so an APK merely dropped in the # docroot would 404 — the request never touches the filesystem. These two # blocks are what make the download work: # # /var/www/blut24.com/web/downloads/blut24-mobile.apk # /var/www/blut24.com/web/downloads/version.json ← in-app updater # /var/www/blut24.com/web/downloads/history.json ← in-app updater # /var/www/blut24.com/web/downloads/history/*.apk ← superseded builds # # `downloads/` is a real directory next to `current/` and `releases/`. A # deploy only swaps the `current` symlink, so it is never touched. It is # published by scripts/upload-mobile-update.sh in the erp-nuxt-frontend repo. location /downloads/ { root /var/www/blut24.com/web; autoindex off; # version.json / history.json are replaced on every release — never let a # proxy or browser pin an old manifest. add_header Cache-Control "public, max-age=300, must-revalidate" always; add_header X-Content-Type-Options "nosniff" always; try_files $uri =404; } # Regex locations win over the prefix match above, so this is what actually # serves the APK: correct Android MIME type + forced download. Without the # explicit type nginx falls back to application/octet-stream, which some # Android browsers refuse to hand to the package installer. location ~ ^/downloads/(?[A-Za-z0-9._-]+\.apk)$ { root /var/www/blut24.com/web; types { application/vnd.android.package-archive apk; } default_type application/vnd.android.package-archive; add_header Content-Disposition 'attachment; filename="$apk_file"' always; # A release overwrites blut24-mobile.apk in place — keep the TTL short. add_header Cache-Control "public, max-age=300, must-revalidate" always; add_header X-Content-Type-Options "nosniff" always; try_files $uri =404; } # The registration form is the only write path; keep it from being hammered. location /api/registration/ { limit_req zone=blut24_reg burst=10 nodelay; proxy_pass http://blut24_landing; 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 ""; } client_max_body_size 2m; gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml; gzip_min_length 1024; access_log /var/log/nginx/blut24-landing.access.log; error_log /var/log/nginx/blut24-landing.error.log; } # The blut24_reg zone used above is declared in /etc/nginx/conf.d/blut24-limits.conf, # which bootstrap-server.sh writes. It has to live there rather than here because # limit_req_zone is only valid directly inside http{} — putting it in this file # makes `nginx -t` fail.