The live Permissions-Policy header denies features with an empty allowlist
(feature=() — grant to nobody). That header does not send reports by
itself.
Unlike CSP, Permissions-Policy has no report-uri. Reporting uses the
Reporting API: a named endpoint in Reporting-Endpoints, plus a per-feature
report-to parameter.
Reuse the existing CSP collector:
https://pnspsuat.gld.gov.hk/api/csp-reporthttps://pnsps.gld.gov.hk/api/csp-reportBackend: CspReportController (POST /csp-report) already logs the raw JSON
and Content-Type and returns 204. No backend change is required.
See also: csp-apache.conf.md
report-uriThis CSP-style trailer does not produce Permissions-Policy reports:
# Wrong — browsers ignore report-uri on Permissions-Policy
Header always set Permissions-Policy "camera=(), geolocation=(); report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
report-to is a parameter of each feature, not a global trailing directive.
report-to?Yes, if you want a report for that feature.
feature=() — still blocked, but silent (no report)feature=();report-to=csp-endpoint — blocked and reportedThere is no all=() reporter and no header-level report-to that applies to
every feature. Add ;report-to=csp-endpoint only on features you want in the
log. Leave the rest as () if you do not need those reports.
# camera is reported; geolocation is still blocked, but not reported
Header always set Permissions-Policy "camera=();report-to=csp-endpoint, geolocation=()"
Keep the current deny-all policy. Add Reporting-Endpoints, then append
;report-to=csp-endpoint on each feature you want to monitor.
Copy the feature list from the live header; only the report-to parameter is
new. Example (UAT):
Header always set Reporting-Endpoints "csp-endpoint=\"https://pnspsuat.gld.gov.hk/api/csp-report\""
Header always set Permissions-Policy "\
accelerometer=();report-to=csp-endpoint, \
autoplay=();report-to=csp-endpoint, \
camera=();report-to=csp-endpoint, \
display-capture=();report-to=csp-endpoint, \
encrypted-media=();report-to=csp-endpoint, \
fullscreen=();report-to=csp-endpoint, \
geolocation=();report-to=csp-endpoint, \
gyroscope=();report-to=csp-endpoint, \
magnetometer=();report-to=csp-endpoint, \
microphone=();report-to=csp-endpoint, \
midi=();report-to=csp-endpoint, \
payment=();report-to=csp-endpoint, \
picture-in-picture=();report-to=csp-endpoint, \
publickey-credentials-get=();report-to=csp-endpoint, \
screen-wake-lock=();report-to=csp-endpoint, \
usb=();report-to=csp-endpoint, \
web-share=();report-to=csp-endpoint, \
xr-spatial-tracking=();report-to=csp-endpoint"
PROD: same headers, with
https://pnsps.gld.gov.hk/api/csp-report
Because deny-all is already enforced, put report-to on Permissions-Policy
(not only Report-Only). Those reports have disposition: "enforce".
Use Permissions-Policy-Report-Only only when testing a restriction that is
not already denied by the enforcing header. Reports have
disposition: "report".
Header always set Reporting-Endpoints "csp-endpoint=\"https://pnspsuat.gld.gov.hk/api/csp-report\""
Header always set Permissions-Policy-Report-Only "geolocation=();report-to=csp-endpoint"
Report-Only cannot re-enable a feature already denied by Permissions-Policy.
Keep CSP on report-uri as it is. The collector URL is shared; the body is not.
| Source | Content-Type |
Body |
|---|---|---|
CSP report-uri |
application/csp-report |
{ "csp-report": { … } } |
| Permissions-Policy | application/reports+json |
JSON array, type = permissions-policy-violation |
Example Permissions-Policy report:
[{
"type": "permissions-policy-violation",
"url": "https://pnspsuat.gld.gov.hk/",
"body": {
"disposition": "enforce",
"featureId": "geolocation",
"message": "Permissions policy violation: geolocation access has been blocked because of a permissions policy applied to the current document."
}
}]
Chrome often serializes the feature as policyId instead of featureId. Filter
logs on permissions-policy-violation vs csp-report so the two streams stay
distinct.
Optional: also point CSP at the same named endpoint (CSP report-uri remains
for older browsers):
Header always set Content-Security-Policy-Report-Only "…; report-uri https://pnspsuat.gld.gov.hk/api/csp-report; report-to csp-endpoint"
() and usually send nothing./api/csp-report needs no extra CORS setup. The endpoint is
already unauthenticated and CSRF is disabled.Permissions-Policy just to silence a report.
Only grant a feature if the application itself needs it.