---
title: "Refuse odd requests from other sites · Agent Wranglers"
url: https://agentwranglers.se/playbook/application-security/browser-security-headers/fetch-metadata
id: APS-29
level: Recommended
updated: 2026-10-27
build: a2a6bdfed830
tokens: 868
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Refuse odd requests from other sites

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Browser security headers](https://agentwranglers.se/playbook/application-security/browser-security-headers.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: APS-29
- Level: Recommended
- Chapter: [Browser security headers](https://agentwranglers.se/playbook/application-security/browser-security-headers.md)
- Rule: Browsers send `Sec-Fetch-*` headers that say where a request came from. Use them to refuse cross-site requests that are not normal page visits.
- Check: `curl -s -o /dev/null -w '%{http_code}' -H 'Sec-Fetch-Site: cross-site' -H 'Sec-Fetch-Mode: cors' URL` prints 403. The same request with `Sec-Fetch-Mode: navigate` and `Sec-Fetch-Dest: document` prints 200.
- How: In middleware, allow requests with no `Sec-Fetch-Site`, or with `same-origin`, `same-site` or `none`. Also allow cross-site `GET` navigations, except into `object` or `embed`. Answer 403 to the rest. Exempt public APIs and files meant for other sites. It backs up the token or origin check in [Block cross-site form posts](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cross-site-requests.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
function fetchMetadata(req, res, next) {
  const site = req.get('Sec-Fetch-Site');
  if (!site || ['same-origin', 'same-site', 'none'].includes(site)) return next();
  const dest = req.get('Sec-Fetch-Dest');
  if (req.method === 'GET' && req.get('Sec-Fetch-Mode') === 'navigate' && dest !== 'object' && dest !== 'embed') return next();
  res.status(403).end();
}
```

### Python example

```python
# Flask
@app.before_request
def fetch_metadata():
    site = request.headers.get("Sec-Fetch-Site")
    if not site or site in ("same-origin", "same-site", "none"):
        return None
    navigating = request.method == "GET" and request.headers.get("Sec-Fetch-Mode") == "navigate"
    if navigating and request.headers.get("Sec-Fetch-Dest") not in ("object", "embed"):
        return None
    abort(403)
```

## Sources

- [W3C Fetch Metadata Request Headers](https://www.w3.org/TR/fetch-metadata/)

## Related rules

- APS-18 [Block cross-site form posts](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cross-site-requests.md) in Application security

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/application-security/browser-security-headers/fetch-metadata">Agent Wranglers application security playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Refuse to be framed](https://agentwranglers.se/playbook/application-security/browser-security-headers/frame-ancestors.md)
- Next: [Keep other sites' windows apart](https://agentwranglers.se/playbook/application-security/browser-security-headers/cross-origin-isolation.md)
- [Browser security headers](https://agentwranglers.se/playbook/application-security/browser-security-headers.md)
