<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
</IfModule>

# Caching: without explicit headers browsers fall back to heuristic
# caching and keep serving assets of an earlier release.
<IfModule mod_headers.c>
  # The HTML entry points are unhashed and reference the content-hashed
  # bundles of the *current* release, so they must never be reused from
  # the cache without asking — a stale index.html would point at files
  # the new release no longer contains.
  <FilesMatch "\.html$">
    Header set Cache-Control "no-cache, must-revalidate"
  </FilesMatch>

  # The OIDC silent-renew script carries no content hash either — it is
  # loaded by name from silent-renew.html — so it needs the same rule.
  # Left to heuristic caching, a browser keeps running the copy of an
  # earlier release, and a fix to the token renewal would reach existing
  # users days late or not at all.
  <FilesMatch "^silent-renew\.js$">
    Header set Cache-Control "no-cache, must-revalidate"
  </FilesMatch>

  # Content-hashed bundles are immutable: any change gives them a new
  # name, so they can be cached indefinitely.
  <FilesMatch "\.[0-9a-f]{8,}\.(bundle|chunk)\.js$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>
</IfModule>

# Security headers (requirements/REQ_006_security.md). This file is the
# only place they can be set: the app is a static bundle behind the
# managed Apache, there is no server-side code to set them — and
# frame-ancestors is not valid in a <meta http-equiv> anyway, so
# public/index.html could not stand in for it.
#
# `always`, so that error responses carry them too: a chunk the browser
# asks for after a release answers 404 through the SPA rewrite, and that
# response is a document like any other.
#
# The policy is one line on purpose. Apache continues a directive over a
# trailing backslash, but a single space after that backslash turns the
# whole file into a 500 for the entire site, and .htaccess offers no
# syntax check before it goes live.
<IfModule mod_headers.c>
  Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'@CONNECT_ORIGINS@; frame-src 'self'@FRAME_ORIGINS@; frame-ancestors 'self'; base-uri 'none'; form-action 'self'; object-src 'none'"

  # 'self' and SAMEORIGIN rather than 'none' and DENY, and this is not a
  # weakening: what they must stop is a *foreign* page framing the app to
  # steer the victim's click onto a write action, and that they stop.
  # Forbidding same-origin framing as well would lock the app out of its
  # own token renewal — oidc-client-ts renews by loading silent-renew.html
  # into a hidden iframe, and that page is served from here, by this very
  # policy (see src/features/auth/methods/keycloak/KeycloakAuthProvider.jsx).
  # Framing from our own origin needs an XSS on our own origin first, at
  # which point clickjacking is no longer the worry.
  Header always set X-Frame-Options "SAMEORIGIN"

  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "same-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"

  # Deliberately no Strict-Transport-Security: the managed server already
  # sends it (max-age=15552000; includeSubDomains, verified against the
  # running instance). Setting it here as well would emit the header
  # twice with conflicting values, because `always` writes to a different
  # header table than the one the platform's directive uses.
</IfModule>

# THIS FILE IS A TEMPLATE — it is not deployed as it stands.
#
# @CONNECT_ORIGINS@ and @FRAME_ORIGINS@ above are the origins the app
# talks to: its backend, and the Keycloak realm where it has one. Both
# are build-time values out of the env file and differ per environment
# (see .env.example), so no fixed text here could be right for more than
# one of them. `tools/remote frontend deploy` substitutes them from the
# very env file the bundle is built from and writes the result into the
# release — see renderSecurityPolicy there, and the tests in
# test/security_headers.test.js.
#
# Deploying this file unrendered would leave a literal '@CONNECT_ORIGINS@'
# in the policy; the browser then drops that directive and the app can
# reach nothing. renderSecurityPolicy refuses to finish if a placeholder
# survives.
