# -------------------------------- # Base: safe in most environments # -------------------------------- Options -Indexes DirectoryIndex index.html Require all denied RewriteEngine On # If you want forced HTTPS behind a proxy, keep this off here and do it at the proxy #RewriteCond %{HTTPS} off #RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # MIME types (fonts/SVG/ESM) AddType font/woff2 .woff2 AddType font/woff .woff AddType image/svg+xml .svg AddType application/javascript .mjs # Security headers Header always set X-Frame-Options "SAMEORIGIN" Header always set X-XSS-Protection "1; mode=block" Header always set X-Content-Type-Options "nosniff" # HSTS: only if HTTPS (prevents mixed local dev warnings) Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" "expr=%{HTTPS} == 'on'" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()" Header always set X-Download-Options "noopen" Header always set Expect-CT "max-age=86400, enforce" # Nice extra hardening (same-origin resource sharing) Header always set Cross-Origin-Resource-Policy "same-origin" Header always set X-Permitted-Cross-Domain-Policies "none" # CSP (modules, workers, blobs already accounted for) Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; media-src 'self' blob:; worker-src 'self' blob:; frame-ancestors 'self'; object-src 'none'; base-uri 'self'; form-action 'self'" # Caching SetEnvIfNoCase QUERY_STRING "(^|&)v=" has_version_param=1 # HTML/PHP: no cache (app shell) Header set Cache-Control "no-cache, no-store, must-revalidate" Header set Pragma "no-cache" Header set Expires "0" # version.js is your source-of-truth; keep it non-cacheable so dev/CI flips show up Header set Cache-Control "no-cache, no-store, must-revalidate" Header set Pragma "no-cache" Header set Expires "0" # Unversioned JS/CSS (dev): 1 hour Header set Cache-Control "public, max-age=3600, must-revalidate" env=!has_version_param # Unversioned static assets (dev): 7 days Header set Cache-Control "public, max-age=604800" env=!has_version_param # Versioned assets (?v=...): 1 year + immutable Header set Cache-Control "public, max-age=31536000, immutable" env=has_version_param # Compression (if modules exist) BrotliCompressionQuality 5 AddOutputFilterByType BROTLI_COMPRESS text/html text/css application/javascript application/json image/svg+xml AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json image/svg+xml # Disable TRACE RewriteCond %{REQUEST_METHOD} ^TRACE RewriteRule .* - [F]