{
	email {$ACME_EMAIL}
	admin off
	servers {
      listener_wrappers {
        proxy_protocol {
          timeout 2s
          allow {$TRUST_PROXY}
        }
        tls
      }
      trusted_proxies static {$TRUST_PROXY}/32
      client_ip_headers X-Forwarded-For X-Real-IP
    }
}

# Internal only, for the container healthcheck. Never published in compose.
:8080 {
	respond /healthz "ok" 200
	respond "not found" 404
}

{$SITE_ADDRESS} {
	root * /srv
	encode zstd gzip

	# The service worker and the HTML shell must be revalidated on every request.
	# If a browser is allowed to serve sw.js from its own cache, a new build is
	# never discovered and the in-app update prompt can never fire — the app would
	# silently freeze on whatever version it first installed.
	@volatile path / /index.html /sw.js /manifest.webmanifest
	header @volatile Cache-Control "no-cache, must-revalidate"

	# Content-hashed filenames; the name changes whenever the bytes do.
	@hashed path /assets/* /workbox-*.js
	header @hashed Cache-Control "public, max-age=31536000, immutable"

	# Icons keep stable names across builds, so cap them at a day rather than
	# forever, otherwise a replaced icon never surfaces.
	@icons path *.png *.svg *.ico
	header @icons Cache-Control "public, max-age=86400"

	# Some clients reject a manifest that is not served with this exact type.
	@manifest path /manifest.webmanifest
	header @manifest Content-Type "application/manifest+json"

	header {
		X-Content-Type-Options nosniff
		X-Frame-Options DENY
		Referrer-Policy strict-origin-when-cross-origin
		-Server
	}

	# The app routes on the hash, so this only catches someone typing a stray path.
	try_files {path} /index.html
	file_server
}
