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