Fetch Metadata turns request context into an authorization input
Many web attacks reach a state-changing endpoint through a request shape the application never intended. A page may expect a JSON POST from its own JavaScript, then accept the same action from a cross-site form, an image load, or a top-level navigation. Fetch Metadata headers give the server a way to inspect that request context before the business logic runs.
The specification defines headers that describe how a request was initiated. Sec-Fetch-Site identifies the relationship between the initiator and the target. Sec-Fetch-Mode describes the request mode, and Sec-Fetch-Dest identifies the expected destination. Sec-Fetch-User indicates whether a navigation was triggered by user activation. Browsers add these headers so a server can make an early decision about whether the request fits the endpoint's contract.
The useful boundary
Fetch Metadata is most valuable at endpoints with a narrow expected context. An internal API that should only receive same-origin fetch calls can reject a cross-site navigation before authentication or business logic. A browser-only download endpoint can require a document or image destination. A session-changing GET can be rejected because state changes should not depend on a navigation side effect.
The headers do not replace authentication or CSRF tokens. An attacker who can make a request from the same origin, abuse an open redirect, or reach the endpoint outside a browser still needs to be handled by the normal authorization model. The headers reduce the number of valid request shapes, which makes the remaining checks easier to reason about.
A conservative policy
Start in monitoring mode. Record the observed Sec-Fetch-* values by endpoint and method, then compare them with the intended callers. A same-origin application programming interface may legitimately see same-origin, cors, navigate, or no-cors depending on how it is used. Do not assume that every request lacking a header is malicious. Older browsers, command-line clients, health checks, and some middleware paths may omit the headers.
For a state-changing API, an explicit policy can require a JSON-oriented request and a same-origin relationship. For a sensitive navigation endpoint, require a top-level navigation with user activation when that is the real workflow. Return a clear denial and log the relevant headers without logging credentials or request bodies.
Example policy logic, expressed as pseudocode rather than a framework-specific implementation:
if method changes state:
reject if Sec-Fetch-Site is cross-site
reject if Sec-Fetch-Mode is navigate and endpoint expects fetch
reject if Sec-Fetch-Dest is not in the endpoint's allowed set
The exact values should come from observed traffic, not from a generic blocklist.
Failure modes and deployment concerns
Proxy chains can strip or rewrite request headers. Test the policy after TLS termination, ingress routing, and application middleware. If a reverse proxy adds a second set of headers or forwards a client-supplied value, the application may make a decision from the wrong source. Browser-set Fetch Metadata headers are intended to be handled by the browser, but non-browser clients can still send arbitrary headers. That is another reason to keep authorization server-side.
Caching also deserves review. A response that varies on Sec-Fetch-Site must not be shared across request contexts unless the cache key includes that variation. A denial response should not be cached for a legitimate navigation. Use the appropriate Vary behavior and test through the actual CDN or reverse proxy.
What to monitor
Track denials by endpoint, source relationship, and destination. A sudden rise in cross-site denies can indicate a broken integration, a new frontend deployment, or an attack campaign. A sudden drop in requests that carry the headers can indicate a proxy change or a client population outside the browser. Keep a separate audit trail for requests that reach the business logic after the context check passes.
Fetch Metadata is useful because it gives the server a low-cost way to distinguish request intent from request authenticity. It should be one part of a layered endpoint contract: authenticated identity, authorization, CSRF protection where applicable, input validation, and an explicit policy for how the endpoint may be called.
References
- W3C Web Application Security Working Group, Fetch Metadata Request Headers
- MDN, Cross-Origin Resource Sharing
Top comments (0)