#41232·magento2

CSP Report-To header is built from report_uri, silently losing reports from Reporting API browsers

Author: ScottHelmeCreated Sep 6, 2026Updated Sep 18, 2026
LabelsIssue: ConfirmedReproduced on 2.4.xComponent: CspPriority: P2Area: Other Developer ToolsReported on 2.4.9

Preconditions and environment

Magento Open Source 2.4.9. Present since 2.4.7, and in the backports: 2.4.4-p9, 2.4.5-p8, 2.4.6-p6.

A CSP report URI configured under Stores > Configuration > Security > Content Security Policy, pointing at any collector that serves the Reporting API on a different address to its classic report-uri endpoint.

Steps to reproduce

  1. Set Storefront Default > Report URI to a collector's report-uri endpoint.

  2. bin/magento cache:flush

  3. Request any storefront page and inspect the response headers:

    curl -sI https://store.example/ | grep -iE '^(report-to|content-security-policy)'

Expected result

The report-to group resolves to an endpoint capable of accepting the Reporting API's application/reports+json payload — either by not emitting report-to at all, or by allowing the Reporting API endpoint to be configured separately from report_uri.

Actual result

Magento\Csp\Model\Policy\Renderer\SimplePolicyHeaderRenderer::render() builds the Report-To header out of the report_uri value:

if ($config->getReportUri() && !$response->getHeader('Report-To')) {
    $reportToData = ['group' => 'report-endpoint', 'max_age' => 10886400,
                     'endpoints' => [['url' => $config->getReportUri()]]];
    $value .= ' report-uri ' . $config->getReportUri() . ';';
    $value .= ' report-to '  . $reportToData['group'] . ';';
    $response->setHeader('Report-To', json_encode($reportToData), true);
}

This assumes one address serves both mechanisms. It usually does not. The classic mechanism posts application/csp-report to the report-uri endpoint; the Reporting API posts a batched application/reports+json array to a Reporting API endpoint, and collectors commonly expose these as two separate addresses.

Per CSP Level 3, a user agent that supports report-to uses it and ignores report-uri entirely. So on a correctly configured store, Chromium browsers post to an endpoint that cannot accept the payload, while Firefox and Safari continue to work via report-uri.

Nothing surfaces the failure. The configuration looks correct, no error is raised, and the merchant sees a partially populated report set with no indication that a browser family is missing from it.

Additional information

Two things make this hard to notice: the store looks correctly configured, and the browsers that still work are the ones producing the smaller share of traffic. It shows up as odd data rather than as a broken configuration.

Suggested fixes, in order of preference:

  1. Add a separate report_to configuration field per area, falling back to report_uri only when it is unset.
  2. Emit the report-to directive and Report-To header only when such a field is set.
  3. At minimum, document that the two must resolve to the same address.

A plugin on the renderer works around it, but every affected collector's users need the same workaround independently, which suggests it belongs in core.

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.