WordPress CSRF Protection: How Nonces, SameSite, and Referer Checks Stop Forged Requests
September 16, 2026

CSRF (cross-site request forgery) makes a logged-in admin’s browser send a request they did not intend. It sits in the OWASP Top 10 under Broken Access Control (A01), and its CWE number is 352.
How the attack runs
- You’re logged into your WordPress admin.
- In another tab, you open a page the attacker controls, from a link, an ad or a compromised site.
- That page sends a hidden request to your site: a form submission, an image request or a script call.
- Your browser attaches your session cookie. Your site treats the forged request as yours and acts on it.
Forged-request flaws keep turning up in WordPress plugins. Some were in two-factor setup code.
Three controls that stop a forged request
| Control | How it works | Limit |
|---|---|---|
| Nonces | A short-lived token tied to one action and one user. The server checks it with wp_verify_nonce() or check_admin_referer() before acting. An attacker’s page can’t create a valid one | Only protects handlers that check it. Core checks its own. Plugins and custom code have to do it themselves |
| SameSite cookies | With Lax or Strict, the browser doesn’t send your session cookie on cross-site POST requests, which is the main route CSRF uses | Doesn’t fix a handler that never verifies a token |
| Referer checking | check_admin_referer() confirms the request came from your own site | Referer headers can be stripped, so it can’t stand alone |
Verify the nonce on every request that changes state in custom endpoints.
How to harden your site against CSRF
- Set SameSite on your cookies. Use at least SameSite=Lax for session and authentication cookies. This lives in your security headers.
- Add the rest of your security headers. See our guide to website security headers.
- Add a content security policy. It limits what an injected page can do.
- Audit nonces in your custom code. Every handler that changes state should call
wp_verify_nonce()orcheck_admin_referer(). - Make admin endpoints less predictable. CSRF payloads target default WordPress paths. If you change your wp-admin URL, canned payloads can’t find their target.
What it does and doesn’t cover
WP Ghost reduces the attack surface around login and admin. It does not replace nonce checks.
| Does | Doesn’t |
|---|---|
| Rejects forged requests, even from a real admin’s browser | Fix a plugin that ships without nonce checks. That flaw stays until the plugin is patched |
Keep plugins updated and pick ones that verify requests. The complete hack prevention guide shows how this fits with other defenses.
FAQ
Does WordPress have built-in CSRF protection?
Partly. Core provides nonces, and custom code has to check them.
Will SameSite cookies break my logins or my site?
Not with SameSite=Lax, the recommended default.
Can a firewall stop CSRF attacks?
Only partly. A forged request can look like a normal logged-in one.
How is CSRF different from XSS?
CSRF tricks a victim’s browser into sending a forged request to your site.