2026-06-15 16:23:14 -04:00
|
|
|
# nginx config for serving SecureBit.chat (static PWA) on Fly.io.
|
|
|
|
|
# Mirrors the behavior of the Apache .htaccess: correct JS MIME for ES modules
|
|
|
|
|
# (.jsx/.mjs), no-cache for the app shell / service worker / versioning files,
|
|
|
|
|
# long-immutable cache for hashed/static assets, security headers, SPA fallback.
|
|
|
|
|
|
|
|
|
|
worker_processes auto;
|
|
|
|
|
events { worker_connections 1024; }
|
|
|
|
|
|
|
|
|
|
http {
|
|
|
|
|
include /etc/nginx/mime.types;
|
|
|
|
|
default_type application/octet-stream;
|
|
|
|
|
|
|
|
|
|
# ES modules must be served as JavaScript. nginx's default mime.types maps
|
|
|
|
|
# .js but not .mjs/.jsx — declare them explicitly (this overrides .js too).
|
|
|
|
|
types {
|
|
|
|
|
application/javascript js mjs jsx;
|
|
|
|
|
text/css css;
|
|
|
|
|
application/json json map;
|
|
|
|
|
application/manifest+json webmanifest;
|
|
|
|
|
font/woff2 woff2;
|
|
|
|
|
font/woff woff;
|
|
|
|
|
image/svg+xml svg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendfile on;
|
|
|
|
|
tcp_nopush on;
|
|
|
|
|
server_tokens off;
|
|
|
|
|
|
|
|
|
|
gzip on;
|
|
|
|
|
gzip_vary on;
|
|
|
|
|
gzip_min_length 256;
|
|
|
|
|
gzip_types text/plain text/css application/javascript application/json image/svg+xml font/woff2;
|
|
|
|
|
|
|
|
|
|
# Decide Cache-Control from the request path. Keeping all add_header calls at
|
|
|
|
|
# one level avoids nginx's header-inheritance reset between blocks.
|
|
|
|
|
map $uri $sb_cache {
|
|
|
|
|
default "public, max-age=31536000, immutable";
|
|
|
|
|
~^/index\.html$ "no-cache, no-store, must-revalidate";
|
|
|
|
|
~^/$ "no-cache, no-store, must-revalidate";
|
|
|
|
|
~^/sw\.js$ "no-cache, no-store, must-revalidate";
|
|
|
|
|
~^/manifest\.json$ "no-cache, no-store, must-revalidate";
|
|
|
|
|
~^/meta\.json$ "no-cache, no-store, must-revalidate";
|
|
|
|
|
~^/dist/ "no-cache, no-store, must-revalidate";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server {
|
|
|
|
|
listen 8080;
|
|
|
|
|
listen [::]:8080;
|
|
|
|
|
server_name _;
|
|
|
|
|
root /usr/share/nginx/html;
|
|
|
|
|
index index.html;
|
|
|
|
|
|
|
|
|
|
# Security headers (frame-ancestors complements the in-page CSP meta tag).
|
|
|
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
|
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
|
add_header X-Frame-Options "DENY" always;
|
|
|
|
|
add_header Content-Security-Policy "frame-ancestors 'none';" always;
|
|
|
|
|
add_header Cache-Control $sb_cache always;
|
|
|
|
|
add_header Service-Worker-Allowed "/" always;
|
|
|
|
|
|
2026-06-15 16:30:39 -04:00
|
|
|
# Real asset files must return 404 when missing — never fall back to the
|
|
|
|
|
# HTML shell, which would be served with the wrong content type and break
|
|
|
|
|
# module/script loading (e.g. a missing config/ice-servers.js).
|
|
|
|
|
location ~* \.(js|mjs|jsx|css|json|map|woff2?|ttf|otf|png|jpe?g|gif|webp|svg|ico|mp3|mp4|webm)$ {
|
|
|
|
|
try_files $uri =404;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# SPA-style fallback so unknown navigation routes still load the app shell.
|
2026-06-15 16:23:14 -04:00
|
|
|
location / {
|
|
|
|
|
try_files $uri $uri/ /index.html;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|