# CSRF (Cross-Site Request Forgery)

> CSRF tricks a logged-in browser into sending an unwanted request to your site. See how it works, why CORS does not stop it and which defenses actually do.

Source: https://howhttpworks.com/glossary/csrf
Last reviewed: 2026-10-04

> **TL;DR:** CSRF works because browsers attach cookies automatically, even to requests triggered by another site. Defend with `SameSite` cookies plus a CSRF token or `Origin` check on every state-changing request.

Cross-site request forgery (CSRF) is an attack in which a malicious page makes a victim's browser send a request to a site where the victim is logged in. Because the browser includes that site's cookies automatically, the request is authenticated, and the server cannot tell it from a deliberate action unless it checks for something the attacker cannot supply.

## How it plays out

A page on `evil.example` contains a self-submitting form:

```html
<form action="https://bank.example/transfer" method="POST">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit()</script>
```

If `bank.example` uses a session cookie without `SameSite` protection and checks nothing else, the request arrives like this:

```http
POST /transfer HTTP/1.1
Host: bank.example
Origin: https://evil.example
Cookie: session=abc123
Content-Type: application/x-www-form-urlencoded

to=attacker&amount=1000
```

The `Origin` header gives the game away, but only if the server looks at it.

## Defenses that work

- **Reject on `Origin` or `Sec-Fetch-Site`.** For state-changing methods, require `Origin` to match your own origin, or `Sec-Fetch-Site` to be `same-origin` (or `none`).
- **CSRF tokens.** A per-session or per-request random value in a hidden field or custom header that the attacker cannot read.
- **`SameSite=Lax` or `Strict` on the session cookie.** Chromium treats a missing attribute as `Lax`; Firefox and Safari do not by default, so set it explicitly.
- **Use safe methods only for reading.** Lax cookies are sent on top-level `GET` navigations, so a `GET /delete?id=5` endpoint is still exploitable.

## Non-obvious facts

- **CORS is not a CSRF defense.** A cross-origin form POST or a "simple" `fetch` is sent whether or not CORS headers allow reading the response.
- **A custom header works as a defense** (for example `X-Requested-With`) because adding one forces a preflight, which the attacker's origin will fail. It only holds if CORS is configured strictly.
- **Same-site is not same-origin.** A compromised sibling subdomain is same-site, so `SameSite` will not help; see [Same-Site vs Same-Origin](https://howhttpworks.com/glossary/same-site).
- **Login CSRF exists too.** An attacker can log the victim into the attacker's account, so protect the login form as well.

## Go deeper

- [SameSite cookie attribute](https://howhttpworks.com/cookies/same-site)
- [Cookie security guide](https://howhttpworks.com/guides/cookie-security)
- [Origin header](https://howhttpworks.com/headers/origin)
- [HTTP cookie](https://howhttpworks.com/glossary/cookie)
