Security Guide

MCP server Reporting API security — CSP violation reports, Report-To endpoint privacy leaks, report flooding DoS, and Network Error Logging (NEL) topology disclosure

The Reporting API lets browsers automatically send structured reports about policy violations, deprecation warnings, and network failures to a collector endpoint configured via the Report-To HTTP header. For MCP servers, the Reporting API creates a set of non-obvious security risks: violation reports sent to third-party collectors contain sensitive URL and context information that leaks browsing behavior; MCP tool output can trigger artificial CSP violations in bulk, overwhelming the collector in a report-flooding DoS; and Network Error Logging (NEL) reports can expose the topology of internal API services that the MCP server calls, even from a browser context.

How the Reporting API works

The Report-To header (Reporting API v0) and its successor Reporting-Endpoints header (Reporting API v1) configure named collector endpoints that the browser sends reports to. Reports are batched and sent asynchronously — the browser queues them and delivers them in bulk, even after the page is closed (via keepalive). Configured endpoints persist for the header's max_age duration — up to 604800 seconds (7 days) — which means a single page load can configure reporting that persists across all subsequent visits to the origin during that window.

# Reporting API v0 (Report-To header)
Report-To: {"group":"csp-endpoint","max_age":86400,
  "endpoints":[{"url":"https://reporting.example.com/collect"}]}
Content-Security-Policy: default-src 'self'; report-to csp-endpoint

# Reporting API v1 (Reporting-Endpoints header — Chrome 96+)
Reporting-Endpoints: csp-endpoint="https://reporting.example.com/collect"
Content-Security-Policy: default-src 'self'; report-to csp-endpoint

Reports are sent as application/reports+json POST requests with structured JSON bodies that include the report type, URL where the violation occurred, user agent, timestamp, and violation-specific details.

CSP violation report privacy leaks

A CSP violation report contains the blocked-uri field (the URL of the resource that was blocked) and the document-uri field (the URL of the page where the violation occurred). If the MCP client uses query parameters or URL fragments to carry sensitive state (e.g., https://mcp-client.example.com/chat?session=abc123&user=42), this URL appears in violation reports sent to the collector.

If the collector is a third-party service (Sentry, Report URI, Cronitor), this means the third party receives session IDs, user IDs, and other sensitive URL parameters from every CSP violation on the MCP client. For MCP clients where tool output often contains URLs processed as part of tasks (e.g., the user asks the agent to "summarize this document" and pastes a private URL), those URLs may appear in violation reports if the MCP client CSP is misconfigured to trigger violations on user-pasted content.

document-uri truncation: The CSP Level 3 spec recommends that browsers strip the URL query string and fragment from document-uri before sending the report, to reduce privacy leakage. Chrome implements this. However, this is not universally enforced across all browsers and older report-uri (v1) implementations may send full URLs including query strings.

report-uri vs report-to: migration compatibility gap

Directive CSP spec version Browser support Batching Endpoint persistence
report-uriCSP Level 2All browsers (legacy)No — one POST per violationNone — per-request only
report-toCSP Level 3Chrome 70+, Edge 79+; Firefox partial; Safari limitedYes — batched, async, post-unloadmax_age (up to 7 days)

Firefox does not fully support report-to as of 2026 — violations go unreported in Firefox when report-uri is not present. Sites that migrate entirely to report-to lose Firefox violation visibility. The correct migration pattern is to include both directives during a transition period: report-uri /fallback; report-to csp-endpoint. Browsers that support report-to prefer it; others fall back to report-uri.

Report flooding from MCP tool output

An MCP tool that returns HTML or JavaScript that intentionally triggers CSP violations can generate large numbers of violation reports. With report-uri (unbatched), each violation generates an immediate POST request to the collector. Tool output that generates 1000 violations per second — easily achievable with a loop that attempts to load 1000 blocked resources — can overwhelm a reporting collector, causing legitimate violation reports to be dropped or delayed.

<!-- Tool output that floods the CSP reporter (requires no JS execution) -->
<!-- Each img src triggers a CSP violation report if img-src is restricted -->
<img src="https://blocked-1.example.com/x.png">
<img src="https://blocked-2.example.com/x.png">
<!-- ... repeat 10,000 times ... -->

This is a DoS against the monitoring infrastructure, not the MCP server itself. In practice, rate-limiting at the collector (standard for services like Report URI) mitigates the impact, but a self-hosted collector with no rate limiting is vulnerable. Additionally, the flood can mask real violations that get dropped during the flood.

Network Error Logging (NEL) topology disclosure

NEL configures the browser to report network errors (DNS failures, TCP connection failures, TLS errors) when fetching resources. For MCP servers that proxy requests to internal APIs and return those URLs in tool output for the client to fetch, NEL reports sent to a third-party collector can reveal the internal hostnames and IP addresses of those APIs.

# NEL header exposes internal topology to the NEL collector
NEL: {"report_to":"default","max_age":86400,"include_subdomains":false}
Report-To: {"group":"default","max_age":86400,
  "endpoints":[{"url":"https://third-party-nel-collector.com/"}]}

# If the MCP client fetches https://internal-api.corp.skillaudit.dev/data
# and that fetch fails, the NEL report includes:
# { "type": "network-error", "url": "https://internal-api.corp.skillaudit.dev/data", ... }
# This goes to third-party-nel-collector.com — disclosing the internal hostname

The fix is to use a first-party NEL collector — a same-origin endpoint that the MCP server controls — rather than a third-party service. Internal hostnames never leave the organization.

SkillAudit findings for Reporting API

HIGHCSP report-to or report-uri points to a third-party collector (Sentry, Report URI, etc.) — CSP violation reports include document-uri with URL query parameters that may contain session IDs or sensitive state. Score −16.
HIGHNEL configured to report to a third-party collector — failed fetches to internal API hostnames in tool output will be reported to the external collector, disclosing internal network topology. Score −14.
MEDIUMSelf-hosted violation collector has no rate limiting on report ingestion — MCP tool output that generates CSP violations at scale can flood the collector, causing legitimate violations to be dropped. Score −10.
MEDIUMMCP server migrated entirely from report-uri to report-to without keeping report-uri as fallback — CSP violations in Firefox go unreported, masking security events from a significant browser segment. Score −8.
LOWReport-To header max_age set to 604800 (7 days) — a compromise or misconfiguration in the collector endpoint persists for 7 days in browser caches across all users who visited during that window. Score −4.

Run a SkillAudit scan on your MCP server to audit Reporting API endpoint configuration, detect third-party collector data flows, and identify rate-limiting gaps that enable report flooding from tool output.