WordPress “Cookies are blocked”: what it really means and how to fix it

Was this helpful?

Nothing spikes your blood pressure like an exec pinging: “I can’t log into WordPress. It says cookies are blocked.” You try it yourself. Same error. You open DevTools and… the browser looks fine. Cookies work everywhere else. So why is WordPress acting like it’s 2004?

This message is not a diagnosis. It’s WordPress shrugging in a trench coat. The real problem is almost always one of these: headers not reaching the browser, HTTPS being misdetected, a cache serving the wrong response, or a proxy/CDN rewriting things into nonsense. We’ll treat it like an incident: reproduce, capture evidence, isolate the layer, fix the cause, and add guardrails so it doesn’t come back at 2 a.m.

What the message really means (and what it doesn’t)

When WordPress says “Cookies are blocked or not supported by your browser”, it’s rarely your browser. WordPress shows this when the login flow can’t confirm a session cookie round-trip.

In plain terms, WordPress expects this sequence:

  1. Your browser requests /wp-login.php.
  2. The server replies with a Set-Cookie header (test cookie or auth cookie depending on the step).
  3. Your browser sends that cookie back on the next request.
  4. WordPress sees the cookie and proceeds. If it doesn’t, you get the “cookies are blocked” message or a redirect loop.

If the cookie doesn’t come back, WordPress can’t tell whether you’re authenticated, whether you can store cookies at all, or whether the login request is being replayed from cache. WordPress then tosses you that generic message.

What it is usually not

  • Not a browser setting, unless you’re in a very locked-down environment or testing in an unusual privacy mode.
  • Not “WordPress is down”. Most sites still serve pages fine; only login is broken.
  • Not a “clear cookies and try again” permanent fix. If clearing cookies helps, it’s a clue, not a solution.

What it usually is

  • Headers changed or stripped (proxy/CDN/WAF, misconfigured PHP-FPM, wrong caching rules).
  • HTTP/HTTPS confusion (WordPress thinks it’s on HTTP, sets cookies without Secure, or generates redirects incorrectly).
  • Cookie scope mismatch (domain/path mismatch, wrong siteurl and home, mixed hostnames).
  • Cache interference (page cache, reverse proxy cache, object cache, or CDN caching /wp-login.php or redirect responses).
  • Plugin/security layer overreach (rewriting headers, injecting redirects, changing SameSite policies poorly).

Paraphrased idea attributed to Werner Vogels (Amazon CTO): Everything fails sometimes; build systems and habits that assume failure and recover quickly. That applies here. Treat login like a critical path. Observe it, protect it, and don’t let “clearing cookies” become your operational plan.

Cookies are deceptively small for how much chaos they cause. A few concrete facts give you better instincts during troubleshooting.

  • Fact 1: HTTP is stateless by design. Cookies were a pragmatic hack to keep state across requests without redesigning the web.
  • Fact 2: Cookies came out of early Netscape-era web engineering (mid-1990s). They were useful long before they were controversial.
  • Fact 3: A cookie is just a header. Specifically, the server sends Set-Cookie and the client returns Cookie. If a proxy messes with headers, your “browser problem” is actually infrastructure.
  • Fact 4: Modern browsers enforce cookie rules far more strictly than they used to—especially for SameSite, cross-site requests, and third-party contexts.
  • Fact 5: The Secure attribute blocks cookies over HTTP. Great for safety. Terrible for misconfigured TLS termination where the app thinks it’s on HTTP.
  • Fact 6: Many login bugs present as redirect loops, not explicit cookie errors, because the app keeps trying to establish a session.
  • Fact 7: WordPress historically tolerated a lot of weird hosting setups (shared hosting, random ports, odd proxies). That legacy means there are multiple ways to configure the same thing—and multiple ways to misconfigure it.
  • Fact 8: Some CDNs and “optimization” plugins aggressively cache redirects and HTML. If they cache wp-login.php responses, you’re effectively serving a previously generated login flow to everyone. That’s not “optimization”; it’s roulette.

Joke #1: Cookies are tiny files that can ruin your weekend. The second-worst kind of cookie to find in production is the one you didn’t set.

Fast diagnosis playbook (first/second/third)

If you do SRE work long enough, you learn to love a good playbook. Here’s one that finds the culprit fast without wandering through plugin settings like it’s a haunted house.

First: Prove whether Set-Cookie is being sent and returned

  • Capture response headers from /wp-login.php and the POST to /wp-login.php.
  • Confirm the browser (or curl) sends the cookie back on the next request.
  • If Set-Cookie is missing or changes unexpectedly between hops, stop blaming the browser.

Second: Identify the “edge” that owns the headers

  • Is there a CDN? A WAF? A reverse proxy? An ingress controller? Nginx caching layer?
  • Bypass it temporarily (direct origin IP/port, hosts file override, internal network) and compare headers.
  • If origin works but edge fails, you have your suspect.

Third: Validate HTTPS and canonical host assumptions

  • WordPress must agree with reality on: scheme (https), host (www vs apex), and path.
  • Check siteurl and home in the database.
  • Check proxy headers: X-Forwarded-Proto, X-Forwarded-Host, and WordPress’s $_SERVER values.

When to escalate immediately

  • You see cached responses for /wp-login.php or /wp-admin/.
  • Set-Cookie headers are being stripped or rewritten by the edge.
  • The site is behind SSO, corporate proxy, or strict CSP rules that changed recently.

Root causes you can actually fix

1) Wrong scheme or host: WordPress sets cookies for the wrong place

If WordPress thinks it’s running on http:// but users connect via https://, cookies may be issued without the right attributes, redirects may bounce, and the auth cookie may never be accepted.

Common scenarios:

  • TLS terminated at a load balancer, but the origin doesn’t receive X-Forwarded-Proto: https.
  • You’re forcing HTTPS at the edge, but WordPress still generates HTTP URLs internally.
  • Mixed canonical hostnames: the login form posts to example.com, but the cookie is for www.example.com (or vice versa).

2) Caching layers: login should not be “optimized”

Any cache that stores or replays login responses can break cookies. This includes:

  • CDN caching rules that accidentally include /wp-login.php.
  • Nginx fastcgi_cache applied too broadly.
  • WordPress caching plugins caching admin or login pages.
  • Edge rules that cache 301/302 responses too aggressively.

Login is dynamic. Auth is per-user. If you cache it, you’re not speeding up the site; you’re breaking basic reality.

3) SameSite and Secure attributes: modern browsers got stricter

Modern cookie rules are more strict about cross-site contexts. Most WordPress logins are first-party, so you usually avoid SameSite trouble. But the moment you introduce:

  • embedded login frames,
  • SSO flows,
  • OAuth plugins with cross-domain callbacks,
  • or “login from a different domain” setups,

…you can run into cookies being rejected unless attributes are set correctly.

4) Cookie domain/path mismatch caused by config drift

WordPress has multiple ways to decide domain and path for cookies. If your WP_HOME, WP_SITEURL, database values, and actual access URL don’t align, you can easily set cookies for the wrong domain or path.

5) Security plugins, WAFs, and “hardening” that breaks headers

Security products love to rewrite headers. Sometimes they do it correctly. Sometimes they strip Set-Cookie because “privacy”. Or they inject redirects that drop cookies. Or they block POSTs to /wp-login.php under overly broad rules.

6) PHP output before headers (yes, still)

If something echoes output before WordPress sets cookies, PHP may emit “headers already sent” behavior, meaning your Set-Cookie never leaves the server. This often happens with broken themes, custom wp-config.php edits, or plugins that print whitespace/BOM at the top of a file.

7) Clock skew and weird TTL behavior

Cookie expiration depends on time. If servers have clock skew, cookies can appear immediately expired, especially in multi-node setups where one node issues and another validates. It’s rarer, but it’s real.

Joke #2: Time sync is the silent killer—because nothing says “secure login” like a server living in the future.

Hands-on tasks: commands, outputs, and decisions

Below are practical tasks you can run from a host with network access to the site (or from the web server itself). Each includes a realistic output sample and what decision you make based on it. Run them in order if you want a clean evidence trail.

Task 1: Fetch /wp-login.php headers and confirm Set-Cookie exists

cr0x@server:~$ curl -skI https://www.example.com/wp-login.php | sed -n '1,20p'
HTTP/2 200
date: Sat, 27 Dec 2025 11:05:12 GMT
content-type: text/html; charset=UTF-8
cache-control: no-store, no-cache, must-revalidate, max-age=0
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax
x-powered-by: PHP/8.2
server: nginx

What it means: WordPress is emitting a test cookie; that’s good. If you don’t see set-cookie at all, either headers are being stripped upstream or PHP isn’t sending them.

Decision: If missing, bypass CDN/proxy next (Task 3). If present, verify it’s returned (Task 2).

Task 2: Confirm the cookie round-trip works with curl

cr0x@server:~$ curl -sk -c /tmp/cj.txt https://www.example.com/wp-login.php >/dev/null
cr0x@server:~$ cat /tmp/cj.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
www.example.com	FALSE	/	TRUE	0	wordpress_test_cookie	WP%20Cookie%20check
cr0x@server:~$ curl -sk -b /tmp/cj.txt -I https://www.example.com/wp-login.php | grep -i set-cookie
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax

What it means: curl stored and resent the cookie successfully. If the server still complains in the browser, the issue may be caching, redirects, or host/scheme mismatch in the browser path.

Decision: If curl round-trip works but browsers fail, inspect redirects and domain canonicalization (Tasks 4–6).

Task 3: Bypass the CDN/reverse proxy by hitting the origin (or internal LB) directly

cr0x@server:~$ curl -sI http://10.20.30.40/wp-login.php | sed -n '1,15p'
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=UTF-8
Set-Cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; HttpOnly; SameSite=Lax

What it means: Origin sets cookie, but note it’s missing Secure over HTTP. If users access via HTTPS at the edge, WordPress may need to be told it’s HTTPS so it sets correct attributes and URLs.

Decision: Fix HTTPS detection behind proxy (see Tasks 10–11) and ensure edge forwards X-Forwarded-Proto.

Task 4: Check redirect chain for login and admin

cr0x@server:~$ curl -skIL https://example.com/wp-admin/ | sed -n '1,40p'
HTTP/2 301
location: https://www.example.com/wp-admin/
cache-control: max-age=3600
date: Sat, 27 Dec 2025 11:06:11 GMT

HTTP/2 302
location: https://www.example.com/wp-login.php?redirect_to=https%3A%2F%2Fwww.example.com%2Fwp-admin%2F&reauth=1
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax

HTTP/2 200
content-type: text/html; charset=UTF-8

What it means: Redirects are expected, but note cache-control: max-age=3600 on a canonical redirect. If your CDN caches that 301/302 aggressively for login paths, you can get stuck in a loop or served stale responses.

Decision: Add “no cache” rules for /wp-login.php, /wp-admin/, and auth-related redirects at the CDN/proxy.

Task 5: Verify siteurl and home in the database

cr0x@server:~$ mysql -u wpuser -p -D wordpress -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
+-------------+------------------------+
| option_name | option_value           |
+-------------+------------------------+
| home        | https://example.com    |
| siteurl     | https://www.example.com|
+-------------+------------------------+

What it means: Mixed hostnames. This is a classic cookie scope mismatch generator and can produce login loops.

Decision: Make them match your canonical host (usually both https://www.example.com or both apex). Update safely (Task 6) and ensure redirects are consistent.

Task 6: Fix home and siteurl (carefully)

cr0x@server:~$ mysql -u wpuser -p -D wordpress -e "UPDATE wp_options SET option_value='https://www.example.com' WHERE option_name IN ('home','siteurl');"
cr0x@server:~$ mysql -u wpuser -p -D wordpress -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
+-------------+-----------------------+
| option_name | option_value          |
+-------------+-----------------------+
| home        | https://www.example.com |
| siteurl     | https://www.example.com |
+-------------+-----------------------+

What it means: Canonical host aligned. This often resolves cookie domain/path weirdness and redirect loops.

Decision: Retest login. If still broken, move to header inspection at the edge (Task 7) and caching checks (Task 8).

Task 7: Compare edge vs origin Set-Cookie and cache headers

cr0x@server:~$ curl -skI https://www.example.com/wp-login.php | egrep -i 'set-cookie|cache-control|server|via|cf-cache-status|x-cache'
server: cloudflare
cf-cache-status: HIT
cache-control: public, max-age=14400

What it means: A CDN cache hit on /wp-login.php. That’s a smoking crater, not a clue.

Decision: Immediately bypass caching for login/admin paths at the CDN and purge the cached objects. Then retest.

Task 8: Inspect Nginx config for caching of login/admin paths

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/fastcgi_cache/,+25p'
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=PHP:100m inactive=60m;
map $request_uri $skip_cache {
    default 0;
    ~*/wp-admin/ 1;
    ~*/wp-login.php 1;
}
server {
    location ~ \.php$ {
        fastcgi_cache PHP;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-Cache $upstream_cache_status;
    }
}

What it means: This config is doing the right thing: it skips cache for login/admin. If your config lacks these exclusions, you can break auth.

Decision: If missing, add exclusions and reload Nginx. If present, check if the map actually matches your paths (some setups use different prefixes or rewrites).

Task 9: Test whether login/admin responses are being cached anyway

cr0x@server:~$ curl -skI https://www.example.com/wp-login.php | egrep -i 'x-cache|cache-control|set-cookie'
cache-control: no-store, no-cache, must-revalidate, max-age=0
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax
x-cache: MISS

What it means: No-store and a cache miss is healthy. If you see HIT or public caching headers, fix caching rules first.

Decision: If caching is clean, focus on scheme/forwarded headers and cookie attributes.

Task 10: Verify the reverse proxy forwards HTTPS indication

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/proxy_set_header X-Forwarded-Proto/,+10p'
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

What it means: The proxy forwards the scheme it sees. If TLS terminates before this proxy, $scheme might be http even for user HTTPS, which is wrong.

Decision: If TLS terminates at an upstream load balancer, set X-Forwarded-Proto https there, or pass through the original header and trust it carefully.

Task 11: Confirm WordPress sees HTTPS on the origin

cr0x@server:~$ php -r 'var_export([ "HTTPS"=>($_SERVER["HTTPS"]??null), "HTTP_X_FORWARDED_PROTO"=>($_SERVER["HTTP_X_FORWARDED_PROTO"]??null) ]);'
array (
  'HTTPS' => NULL,
  'HTTP_X_FORWARDED_PROTO' => NULL,
)

What it means: In this shell context you don’t have request headers, but it highlights what you’re looking for in real request context: WordPress relies on server variables. If requests don’t populate them, WordPress can misbehave behind proxies.

Decision: Add a small temporary diagnostic endpoint or log forwarded headers at the web server; then adjust proxy config or WordPress proxy awareness (commonly via wp-config.php and trusted proxies patterns).

Task 12: Check for “headers already sent” in PHP/WordPress logs

cr0x@server:~$ sudo tail -n 50 /var/log/php8.2-fpm.log | egrep -i 'headers already sent|warning|notice' | tail
WARNING: [pool www] child 18124 said into stderr: "PHP Warning:  Cannot modify header information - headers already sent by (output started at /var/www/html/wp-content/plugins/bad-plugin/bad.php:1) in /var/www/html/wp-includes/pluggable.php on line 1427"

What it means: A plugin printed output early, so WordPress can’t set cookies/headers reliably.

Decision: Disable the offending plugin (Task 13) and fix/replace it. Don’t “work around” this; it’s broken code.

Task 13: Disable plugins quickly without wp-admin

cr0x@server:~$ cd /var/www/html
cr0x@server:~$ sudo mv wp-content/plugins wp-content/plugins.disabled
cr0x@server:~$ sudo mkdir wp-content/plugins
cr0x@server:~$ sudo chown -R www-data:www-data wp-content/plugins

What it means: WordPress will load with no plugins, which is often enough to restore login. This is blunt. It works.

Decision: If login works now, reintroduce plugins in batches to find the offender. If it still fails, the problem is lower (proxy/cache/config).

Task 14: Check system time sync (for multi-node and expiry weirdness)

cr0x@server:~$ timedatectl status | sed -n '1,12p'
Local time: Sat 2025-12-27 11:08:42 UTC
Universal time: Sat 2025-12-27 11:08:42 UTC
RTC time: Sat 2025-12-27 11:08:42
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active

What it means: Time sync is healthy. If it’s not synchronized (or differs across nodes), cookie expiry and nonce checks can misfire.

Decision: Fix NTP/chrony before you dig deeper. Time drift creates “random” auth issues that waste days.

Task 15: Verify the response isn’t compressing/transforming in a way that breaks headers (proxy sanity)

cr0x@server:~$ curl -skI https://www.example.com/wp-login.php | egrep -i 'content-encoding|set-cookie|vary'
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax
vary: Accept-Encoding

What it means: Normal. If you see exotic header mangling or missing cookies only when a certain “optimization” feature is enabled, that feature is guilty until proven otherwise.

Decision: Turn off the header-transforming feature at the edge and retest. Then re-enable with proper exclusions.

Task 16: Inspect browser-visible cookie attributes via a server-side reproduction (optional but decisive)

cr0x@server:~$ curl -skD - https://www.example.com/wp-login.php -o /dev/null | sed -n '/^Set-Cookie/Ip'
Set-Cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly; SameSite=Lax

What it means: Confirms the exact cookie attributes leaving the server. If browsers still reject it, it’s usually domain mismatch, third-party context, or enterprise policy.

Decision: Compare requested host vs cookie Domain/Path; check for cross-site embedding; test from a clean profile and from another network.

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

The company had a WordPress instance used for press releases and investor updates. It wasn’t “mission critical” until it was—on earnings day, the comms team couldn’t log in. The site rendered fine to the public, which made leadership assume the problem was “user error.” Classic.

The on-call engineer checked the obvious: browser cookies enabled, incognito mode, different laptop. Same error. They grabbed headers with curl and noticed something subtle: the login page response had no Set-Cookie header at the public URL, but it did when hitting the origin directly.

The wrong assumption was that the CDN was “just caching static assets.” It wasn’t. Someone had enabled a rule to cache “all HTML for performance,” with a carve-out for /wp-admin but not /wp-login.php. The CDN cached a login response that never established a valid cookie round-trip, and it served it faithfully to everyone.

The fix was straightforward: bypass caching for login and admin endpoints, purge cache, and add a regression check in their synthetic monitoring to assert Set-Cookie presence on login. The lesson was the real cost: assumptions about layers you don’t regularly touch are expensive when they break.

Mini-story 2: The optimization that backfired

A different org wanted faster Time To First Byte on PHP pages. Someone rolled out Nginx fastcgi_cache on the WordPress cluster. It worked beautifully in staging. Production looked faster. Metrics improved. Everyone congratulated themselves and went to lunch.

Two days later, editors started reporting intermittent login loops. Not constant. Not reproducible on demand. The worst kind of bug: the kind that makes people argue about whether it’s real.

Eventually, an engineer added an X-Cache header and discovered that certain 302 responses around authentication were being cached under conditions they didn’t anticipate. The cache key didn’t include enough variation, and the “skip cache” logic missed a rewritten login endpoint used by a security plugin.

The correction required tightening the cache bypass rules, explicitly excluding wp-login.php, wp-admin, and any login aliases, and ensuring cookies in the request force bypass. Performance stayed mostly improved, but the team learned a rule worth keeping: caching dynamic auth flows is like putting a turbocharger on a bicycle—technically possible, socially irresponsible.

Mini-story 3: The boring but correct practice that saved the day

One enterprise ran WordPress behind a load balancer and two reverse proxies, because “compliance.” They also had a habit that sounded dull: every change to edge rules required a small verification script to pass before rollout.

The script did three checks: fetch /wp-login.php and assert a Set-Cookie header exists; fetch /wp-admin/ and ensure it does not return cacheable headers; and confirm redirects lead to a single canonical host and HTTPS.

A security team pushed an update to their WAF template. It unintentionally stripped Set-Cookie from responses matching a generic “tracking cookie” signature. That would have bricked admin login across a dozen sites.

But the deployment pipeline blocked the change because the verification failed in pre-production. The fix was a WAF exception for WordPress auth cookies and a more precise signature. Nothing dramatic happened in production, which is the best kind of incident. Their boring gate saved the day by preventing an outage nobody would have understood during a leadership call.

Common mistakes: symptom → root cause → fix

This section is the “stop guessing” list. If you see the symptom, jump to the likely root cause and do the fix. Avoid cargo-cult steps.

1) Symptom: “Cookies are blocked” appears immediately after submitting credentials

  • Root cause: Set-Cookie never reaches the browser (stripped by CDN/WAF/proxy) or headers already sent in PHP.
  • Fix: Compare edge vs origin headers; disable CDN caching for login; check PHP-FPM logs for “Cannot modify header information”.

2) Symptom: Infinite redirect loop between /wp-admin and /wp-login.php

  • Root cause: Scheme/host mismatch (siteurl/home misaligned), or proxy misreporting HTTPS.
  • Fix: Align home and siteurl; ensure X-Forwarded-Proto is correct; enforce a single canonical hostname.

3) Symptom: Works on origin, fails on public domain

  • Root cause: Edge layer rewriting/stripping Set-Cookie, caching login responses, or doing aggressive bot protection.
  • Fix: Bypass cache for /wp-login.php and /wp-admin; disable HTML caching; add a WAF rule exception for these paths.

4) Symptom: Works in one browser/profile but not another

  • Root cause: Old cookies with mismatched Domain/Path, or a browser extension altering requests, or strict enterprise policies.
  • Fix: Inspect cookie storage for the domain; remove only WordPress cookies; test in a clean profile; check if login is embedded/cross-site.

5) Symptom: Only some users can log in, often behind corporate networks

  • Root cause: Corporate proxy stripping headers, TLS interception, or policy blocking cookies in certain contexts.
  • Fix: Capture headers from affected networks; provide a non-embedded login; ensure cookies are first-party and use HTTPS consistently.

6) Symptom: Breaks after enabling “force HTTPS” at the edge

  • Root cause: WordPress still thinks it’s HTTP, sets wrong redirects/cookies, or generates mixed URLs.
  • Fix: Forward HTTPS headers properly and configure WordPress to recognize HTTPS behind proxies; align DB URLs.

7) Symptom: Multisite login failures across subdomains

  • Root cause: Cookie domain scoping not matching network setup, plus inconsistent canonicalization across sites.
  • Fix: Ensure consistent domain mapping; avoid mixing subdomain and subdirectory modes accidentally; verify network settings and cookie domain behavior.

8) Symptom: Random “logged out” behavior after login succeeds

  • Root cause: Cache mixing authenticated and unauthenticated states, or node-to-node time drift, or object cache collisions.
  • Fix: Ensure caches bypass on auth cookies; verify NTP; validate object cache configuration and salts.

Checklists / step-by-step plan

Checklist A: One-pass triage (15 minutes)

  1. Reproduce in a clean browser profile. Record exact URL and whether you’re using www or apex.
  2. Check headers on /wp-login.php at the public URL: confirm Set-Cookie exists and cache-control is not public.
  3. Check redirect chain for /wp-admin/. Confirm it lands on one canonical https:// host.
  4. Bypass edge (hit origin/LB directly) and compare Set-Cookie behavior.
  5. Disable plugins if logs show “headers already sent” or if behavior changes per request.

Checklist B: Make the fix durable (the “won’t page me again” plan)

  1. Canonicalize host and scheme: pick one (usually https://www or https:// apex) and stick to it everywhere.
  2. Lock down caching rules:
    • Never cache /wp-login.php, /wp-admin/, or responses that set auth cookies.
    • Bypass cache when request has WordPress auth cookies.
    • Don’t cache 302/301 from auth endpoints unless you deeply understand the blast radius.
  3. Make proxy headers correct: ensure HTTPS is correctly signaled to the origin and only trusted proxies can set forwarded headers.
  4. Add synthetic checks that assert:
    • Set-Cookie exists on /wp-login.php
    • no public caching headers on login/admin
    • redirect chain ends at canonical host
  5. Minimize “login tweaks” plugins. Many are fine, but anything that touches headers, redirects, or cookies should be treated like production middleware.
  6. Keep clocks in sync across nodes, always.

Checklist C: If you must change something during an incident

  1. Purge CDN cache for /wp-login.php and /wp-admin/ (and any login aliases).
  2. Temporarily disable HTML caching/edge optimization features for those paths.
  3. Disable plugins by renaming the plugins directory (fast rollback).
  4. Roll back the last edge/WAF/template change if this started “right after” a deployment.

FAQ

1) Is WordPress lying when it says cookies are blocked?

It’s not lying, it’s being vague. WordPress can’t confirm the cookie round-trip, so it blames “cookies” even if the real issue is your proxy or cache.

2) Why does it work for public pages but not wp-admin?

Public pages can be cached and don’t require an authenticated session. Login does. Anything that breaks Set-Cookie, redirects, or HTTPS detection will show up first in wp-admin.

3) Can a CDN really break WordPress login?

Yes. If it caches /wp-login.php, caches redirects in the login chain, strips Set-Cookie, or applies bot protection that blocks POSTs, you’ll see cookie/login failures.

4) What’s the single most common cause in modern setups?

Caching or “optimization” applied to login/admin endpoints. Second place: HTTPS termination/proxy headers not being conveyed correctly to WordPress.

5) Do I need to change SameSite settings?

Not usually for standard WordPress logins. Consider SameSite when you have cross-domain SSO, embedded login, or external callbacks. If you change it, test across browsers and don’t guess.

6) Why does changing home and siteurl fix it?

Because WordPress uses those values to generate URLs and sometimes to infer cookie scope. If one says example.com and the other says www.example.com, you’re inviting cookie mismatches and redirect loops.

7) I disabled all plugins and it still fails. Now what?

Then it’s likely infrastructure: edge caching, header rewriting, HTTPS detection, or database URL mismatch. Compare edge vs origin headers and check redirect chains.

8) Could time drift really cause login trouble?

Yes, especially in multi-node environments. Cookies, nonces, and session validation can behave like the cookie is “ignored” when timestamps don’t agree.

9) Should I just tell users to clear cookies?

Only as a temporary workaround and only after you’ve gathered evidence. If clearing cookies “fixes” it, you still have a systemic mismatch or caching problem waiting to recur.

10) What if the error only appears on mobile or inside an embedded webview?

Then you may be dealing with third-party cookie restrictions, in-app browser policies, or cross-site flows. Avoid embedded login, keep it first-party, and verify cookie attributes for that context.

Conclusion: next steps that stick

“Cookies are blocked” is WordPress’s way of telling you a session handshake failed. Your job is to figure out which layer lied: the app, the proxy, the cache, or the configuration. The fastest path is evidence, not vibes: capture headers, compare edge vs origin, inspect redirects, and confirm canonical scheme/host.

Do this next, in order:

  1. Run a header check on /wp-login.php and confirm Set-Cookie and no public caching.
  2. Follow the redirect chain for /wp-admin/ and eliminate mixed hostnames/schemes.
  3. Bypass your edge and compare. If origin works, fix the edge rules (cache and header handling).
  4. If you see “headers already sent,” disable the offending plugin and treat it as a code defect, not a configuration quirk.
  5. Add a small synthetic check so you never learn about this problem from a frustrated executive again.
← Previous
PCIe passthrough on Proxmox vs ESXi: GPUs, HBAs, NICs — what’s easier and what’s faster
Next →
Debian 13: Core dumps fill your disk — keep debugging value, drop the bloat

Leave a comment