Self-hosted fonts: no fonts.googleapis.com or fonts.gstatic.com. Static assets (including woff2) are served from 'self'.
Adjust report-uri if your API base path or host differs (UAT example below; PROD uses https://pnsps.gld.gov.hk/api/csp-report).
See also:
frame-src / media-src updatesHeader always set Content-Security-Policy "default-src 'self'; \
base-uri 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
frame-src 'self' data: blob:; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src-elem 'self' 'unsafe-inline'; \
img-src 'self' data:; \
media-src 'self' blob: data:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests"
Same policy plus violation reporting:
Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
base-uri 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
frame-src 'self' data: blob:; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src-elem 'self' 'unsafe-inline'; \
img-src 'self' data:; \
media-src 'self' blob: data:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests; \
report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
frame-src: Required for proof file preview iframes that use data: / blob: URLs (UploadFileTable.js). Without this, framing falls back to default-src 'self' and browsers report frame-src violations (often with empty blocked-uri).style-src-elem: Explicit, alongside style-src, for <link rel="stylesheet"> behaviour in modern browsers.img-src: 'self' data: is enough. The WCAG 2 AA badge is bundled locally (src/assets/images/wcag2AA.png) and no longer loaded from www.w3.org.media-src: blob: for captcha audio object URLs; data: for data-URI media if used.font-src: 'self' data: covers bundled fonts and data: URLs if used.wasm-eval from chrome-extension) will still appear under Report-Only. Do not allowlist those origins.