---
title: "Block cross-site form posts · Agent Wranglers"
url: https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cross-site-requests
id: APS-18
level: Required
applies: accounts
risk: takeover
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1111
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Block cross-site form posts

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

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

- ID: APS-18
- Level: Required
- Chapter: [Authentication and sessions](https://agentwranglers.se/playbook/application-security/authentication-and-sessions.md)
- Rule: Make sure a change of state only comes from your own pages, with `SameSite` cookies plus a token or an origin check.
- Check: Send a POST to a change-of-state route with the test user's cookie and `Origin: https://evil.example.net`, then with `Sec-Fetch-Site: cross-site`. The server refuses both with 403. The same POST with your own origin still works.
- Risk: An account or system taken over
- Applies when: The project has user accounts.
- How: Never change anything on a GET request. For cookie logins, check `Sec-Fetch-Site` on every POST, PUT, PATCH and DELETE, and fall back to the `Origin` header. Refuse when neither proves the request came from your own origin. Or add a token to each form. `SameSite` alone does not stop attacks from your own subdomains. APIs that take a bearer token in a header are not exposed. The website rules [refuse odd requests from other sites](https://agentwranglers.se/playbook/application-security/browser-security-headers/fetch-metadata.md) and [set cookies safely](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cookie-attributes.md) add two more layers.

## 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
const UNSAFE = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
app.use((req, res, next) => {
  if (!UNSAFE.has(req.method)) return next();
  const site = req.get('sec-fetch-site');
  const sameOrigin = site ? site === 'same-origin' : req.get('origin') === 'https://example.com';
  return sameOrigin ? next() : res.sendStatus(403);
});
```

### Python example

```python
# Python with Flask
UNSAFE = {"POST", "PUT", "PATCH", "DELETE"}

@app.before_request
def refuse_cross_site():
    if request.method not in UNSAFE:
        return
    site = request.headers.get("Sec-Fetch-Site")
    same_origin = site == "same-origin" if site else request.headers.get("Origin") == "https://example.com"
    if not same_origin:
        abort(403)
```

## Sources

- [OWASP Cross-Site Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
- [W3C Fetch Metadata Request Headers](https://www.w3.org/TR/fetch-metadata/)

## Related rules

- API-2 [Use each method for what it means](https://agentwranglers.se/playbook/api-design/resources-and-methods/http-methods.md) in API design
- API-81 [Accept GraphQL mutations only by POST with a JSON body](https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-post-mutations.md) in API design
- APS-29 [Refuse odd requests from other sites](https://agentwranglers.se/playbook/application-security/browser-security-headers/fetch-metadata.md) in Application security
- APS-17 [Set cookies safely or not at all](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cookie-attributes.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/authentication-and-sessions/cross-site-requests">Agent Wranglers application security playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Set cookies safely or not at all](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/cookie-attributes.md)
- Next: [Clear cached pages and data when someone signs out](https://agentwranglers.se/playbook/application-security/authentication-and-sessions/clear-site-data-on-sign-out.md)
- [Authentication and sessions](https://agentwranglers.se/playbook/application-security/authentication-and-sessions.md)
