Guide

WordPress site slow for logged-in users only — detecting session floods and cache bypass

Your WordPress site is fast. Pingdom is green. The homepage TTFB is under 200ms. Then a customer support ticket comes in: "Site is unbearably slow." You log in to check — and yes, the dashboard takes 8 seconds to load. Editor pages stall.

1. Problem

Your WordPress site is fast. Pingdom is green. The homepage TTFB is under 200ms.

Then a customer support ticket comes in: "Site is unbearably slow." You log in to check — and yes, the dashboard takes 8 seconds to load. Editor pages stall. Checkout times out. WooCommerce account pages hang.

You log out in another browser. The site is fast again.

This is the classic "wordpress slow only for logged-in users" pattern, and it almost never shows up in synthetic monitoring. Every uptime tool in the world hits your site as an anonymous visitor. Anonymous visitors get the page cache. Logged-in users don't. So when something starts hammering the logged-in path, the public-facing graphs stay flat while half your real users — editors, customers, members — get a broken site.

If you also see a sudden jump in account creation, password resets, or login failures during the same window, this is not a performance bug. This is a session flood, and it has a security dimension you cannot ignore.

2. Impact

Logged-in traffic is the most expensive traffic you serve. Every request bypasses page cache, hits PHP, hits the database, and often hits external services (Stripe, shipping APIs, REST endpoints, the Heartbeat API). A 5x spike in logged-in requests can saturate PHP-FPM workers in seconds.

Three concrete consequences, all happening in parallel:

  • Revenue loss. WooCommerce checkout is a logged-in flow. If /my-account/, /cart/, and /checkout/ are slow or 502'ing, abandoned cart rate climbs immediately. You will not see this in your homepage uptime dashboard.
  • Editorial paralysis. Editors can't save posts. Block editor autosave starts failing. You get Slack messages from the marketing team while engineers stare at green graphs.
  • Security exposure. A session-fixation or credential-stuffing wave creates real authenticated sessions. From the PHP layer, those sessions look like normal logged-in users. They consume real resources, they hit real endpoints, and they may be staging account takeover at scale.

The 4xx/5xx graphs may not move at all. Most of the bad requests are returning HTTP 200 — slowly.

3. Why It’s Hard to Spot

Three properties of WordPress make this class of incident silent:

  • Caching hides the problem from monitoring. Anonymous synthetic checks (Pingdom, UptimeRobot, StatusCake) terminate at Cloudflare/Varnish/LiteSpeed Cache and never touch PHP. Your "uptime" is measuring the wrong layer.
  • HTTP 200 isn't an alert condition. Slow logged-in requests still complete. They return real HTML. Your access log shows status 200 with response times of 6–12 seconds. No 5xx-based alert fires.
  • wp-admin is invisible to most observability stacks. WordPress doesn't emit a structured event when someone hits /wp-admin/admin-ajax.php. The Heartbeat API alone can produce 30+ requests/minute per logged-in tab. A flood of "logged-in" sessions all polling Heartbeat looks identical, in nginx logs, to legitimate editorial activity.

The wp_logged_in_requests_total signal is what makes this problem visible. Without it, the symptom is "users say it's slow." With it, the symptom is "logged-in/total ratio jumped from 4% to 47% at 14:22, cache hit ratio dropped 38%, peak memory crossed 256MB."

4. Cause

The signal that exposes this is wp_logged_in_requests_total, a counter Logystera publishes for every WordPress request that arrives with a valid wordpress_logged_in_* cookie. Compare it against wp_http_requests_total (the all-traffic counter) and you get a ratio: what fraction of your traffic is logged in.

For a typical content site this ratio sits between 1% and 5%. A B2B SaaS portal might run at 60–90%. WooCommerce stores hover around 10–20% during sales. Whatever your baseline is, it's stable hour over hour. The ratio breaks only when one of four things happens:

  1. Account takeover wave. Attackers successfully authenticate stolen credentials. Each successful login produces a wordpress_logged_in_* cookie. They then crawl /wp-admin/, /?author=N, REST endpoints, and member-only URLs at machine speed. wp_logged_in_requests_total rises in lockstep with wp_auth_attempts_total{result="success"}.
  2. Session fixation / forged cookies. A misconfigured plugin or a compromised secret key (AUTH_KEY, LOGGED_IN_KEY in wp-config.php) lets an attacker mint cookies that pass wp_validate_auth_cookie() without ever logging in. You get a flood of "logged-in" requests with no corresponding wp_auth_attempts_total increase. The ratio jumps; the auth attempts do not.
  3. A plugin breaks guest caching. A new plugin sets a session cookie on every visitor (a common offender: poorly written analytics, A/B testing, GDPR consent, or "personalization" plugins). Suddenly your CDN treats everyone as logged-in and bypasses cache. Page cache hit ratio collapses (wp_cache_hit_ratio drops sharply). PHP load explodes because traffic that used to terminate at the edge now hits origin.
  4. Genuine signup spike. A press hit, an influencer campaign, or a launch promo creates a real surge of new registered users. Looks identical to (1) at first glance, but wp_auth_attempts_total{result="success"} rises with user_registered events, and the ratio rise is gradual, not vertical.

wp_logged_in_requests_total is the only signal that disambiguates these from a generic traffic graph. Your CDN sees bytes; your APM sees requests; only a session-aware counter sees the kind of traffic shifting.

5. Solution

5.1 Diagnose (logs first)

Section 5 is the one to bookmark. Section 6 maps causes to fixes; this section maps logs to causes.

Step 1: confirm the ratio anomaly

In Logystera (or your PromQL UI), graph the ratio:

rate(wp_logged_in_requests_total[5m])
  / rate(wp_http_requests_total[5m])

A vertical jump above your trailing 24h baseline (say, 2x the p99) is the alarm. Note the exact start time. You'll use it as a pivot for everything else.

Step 2: pull the matching nginx access lines

WordPress sends two relevant cookies: wordpress_logged_in_ and wp-settings-. Filter the access log for the first one to isolate logged-in traffic:

grep 'wordpress_logged_in_' /var/log/nginx/access.log \
  | awk '$4 >= "[27/Apr/2026:14:20:00"' \
  | awk '{print $1, $7, $9, $NF}' \
  | sort | uniq -c | sort -rn | head -50

What you're looking for:

  • A small number of IPs producing a huge number of requests → likely account takeover or scripted session abuse. This will show up in Logystera as wp_logged_in_requests_total concentrating in a tight client_ip label distribution.
  • A flat distribution of IPs but heavy hits to a single endpoint (e.g. /wp-admin/admin-ajax.php?action=heartbeat) → broken cache plugin keeping real users on PHP path.
  • Logged-in requests to URLs that should never be hit while logged in (/feed/, /wp-sitemap.xml, static asset paths) → cache bypass plugin, not malice.

Step 3: correlate with auth attempts

grep -E 'wp-login\.php|/wp-json/.*\b(auth|login|register)\b' /var/log/nginx/access.log \
  | awk '$4 >= "[27/Apr/2026:14:00:00"' \
  | grep -E ' (200|302) '

This produces the wp_auth_attempts_total{result="success"} signal. Compare it to your ratio anomaly:

  • Auth successes spike together with logged-in ratio → cause (1) account takeover, or cause (4) legit signups. Disambiguate via geo distribution and user agents. Real signups come from diverse, browser-like UAs. Stuffing waves come from datacenter ASNs and headless UAs.
  • Auth successes flat, ratio spikes → cause (2) forged session cookies. This is the most dangerous case. It means your LOGGED_IN_KEY may be compromised or a plugin is calling wp_set_auth_cookie() for unauthenticated users.
  • Auth successes flat, ratio spikes, and wp_cache_hit_ratio drops at the same instant → cause (3) plugin broke guest caching.

Step 4: check PHP error and slowlog

tail -F /var/log/php-fpm/slow.log
grep -E "WordPress database error|Allowed memory size" /var/log/php-fpm/error.log | tail -50

You'll see wp_request_peak_memory_mb climbing as PHP-FPM workers exhaust the pool. Slowlog entries clustered around wp-cron.php, admin-ajax.php?action=heartbeat, or WP_Query are the smoking gun for cache bypass. They produce the php.slow_request signal, and at the tail end, php.fatal with "Allowed memory size exhausted."

Step 5: identify recently activated plugins (cause 3)

SELECT post_title, post_modified
FROM wp_posts
WHERE post_type = 'plugin' OR post_status = 'activate'
ORDER BY post_modified DESC LIMIT 20;

Or via WP-CLI:

wp plugin list --status=active --fields=name,version,update --format=table \
  | head -30
wp option get active_plugins --format=json | jq .

Cross-reference activation timestamps against your ratio jump. A plugin activated at 14:18 that starts a flood at 14:22 is your culprit. The signal trail is wp.state_change{type=plugin_activated} followed minutes later by wp_cache_hit_ratio dropping and wp_logged_in_requests_total rising.

5.2 Root Causes

(see root causes inline in 5.3 Fix)

5.3 Fix

Each fix maps back to which cause it addresses, and which signal should normalize after.

Cause 1 — Account takeover wave. Force-rotate sessions for every user (wp eval 'wp_destroy_all_sessions_for_all_users();' is not built in, but you can clear session_tokens user meta in bulk: DELETE FROM wp_usermeta WHERE meta_key = 'session_tokens';). Force password reset on suspicious accounts. Enable 2FA. Block the source ASNs at the edge. Expected signal recovery: wp_auth_attempts_total{result="failure"} will spike (their replays now fail), then taper. wp_logged_in_requests_total ratio returns to baseline within minutes.

Cause 2 — Forged or fixated sessions. Rotate every key in wp-config.php. The salts at https://api.wordpress.org/secret-key/1.1/salt/ — generate a new set, replace, deploy. This invalidates every existing session globally. Audit any plugin calling wp_set_auth_cookie(); legit plugins use it only inside login flows. Check for filter abuse on auth_cookie and determine_current_user. Expected recovery: wp_logged_in_requests_total drops to near zero for a few minutes (every session is gone), then returns to baseline as legitimate users log back in.

Cause 3 — Plugin breaking guest cache. Identify the culprit via the activation timeline from step 5. Most common offenders write a Set-Cookie header on every request via init or template_redirect. Disable the plugin (wp plugin deactivate ), clear page cache (wp cache flush, plus your CDN), and verify wp_cache_hit_ratio recovers. If you can't disable the plugin, add an exclusion rule in your cache plugin so its cookie doesn't bust the cache. Expected recovery: wp_cache_hit_ratio climbs back above 80% within one cache-warm cycle; wp_request_peak_memory_mb drops as PHP load falls.

Cause 4 — Real signup surge. Scale PHP-FPM workers, raise pm.max_children, add a second app server, or enable object cache (Redis/Memcached) if you haven't. This is the only "good news" cause, but it can still take you down. Expected recovery: ratio stays elevated (correctly), but wp_request_peak_memory_mb and PHP-FPM queue depth normalize.

If you don't yet know which cause you're in, run the safe holding action: enable a Cloudflare rate-limit rule on /wp-login.php (10/min/IP), block requests with empty user-agents, and put /wp-admin/ behind a transient IP allowlist. This buys 30 minutes to investigate without taking the public site down.

5.4 Verify

The verification signal is wp_logged_in_requests_total returning to within the trailing 24-hour band. Specifically:

  • The 5-minute ratio rate(wp_logged_in_requests_total[5m]) / rate(wp_http_requests_total[5m]) returns to within 1.5x of the 24h median.
  • wp_cache_hit_ratio is back above its baseline (typically 75–95% on a content site).
  • wp_request_peak_memory_mb is no longer touching the memory_limit ceiling.
  • wp_auth_attempts_total{result="failure"} is not climbing in parallel.

Give it 30 minutes under normal traffic. Watch the access log:

tail -f /var/log/nginx/access.log | grep -E 'wordpress_logged_in_' \
  | awk '{print strftime("%H:%M:%S"), $7}'

If logged-in requests stay distributed across a normal mix of /wp-admin/, /my-account/, /checkout/ — and not concentrated on one endpoint or one IP — you're clean. If the pattern returns within a few hours, you fixed a symptom and not the cause; go back to step 5 and look harder at plugin activations and at any custom code touching wp_set_auth_cookie.

6. How to Catch This Early

Fixing it is straightforward once you know the cause. The hard part is knowing it happened at all.

This issue surfaces as wp_logged_in_requests_total.

The reason this incident is dangerous is not that it's hard to fix. It's that nothing alerts on it by default. WordPress has no built-in concept of "ratio of logged-in to anonymous traffic." Cloudflare doesn't know which of your visitors carry a session cookie. APM tools see "PHP got slow" hours after the damage is done. Page cache plugins silently report a lower hit rate without flagging it as anomalous.

A logged-in ratio anomaly is exactly the kind of failure logs reveal in real time and dashboards reveal in retrospect. The wp_logged_in_requests_total signal — emitted on every authenticated request by the Logystera WordPress agent — is the one number that lets you ask "is the composition of my traffic changing?" instead of "is the volume changing?"

Logystera detects this by alerting on a sustained deviation from the trailing 24-hour baseline of wp_logged_in_requests_total / wp_http_requests_total, correlated against wp_auth_attempts_total and wp_cache_hit_ratio. The alert fires before users file tickets, with the supporting signals already attached so you know which of the four causes you're in.

You don't need Logystera to see this signal — every line of section 5 works on raw nginx and PHP-FPM logs. You need something watching for the ratio change while you sleep.

7. Related Silent Failures

  • wp_auth_attempts_total spike with low success rate → credential stuffing in progress, often a precursor to the takeover wave that drives a logged-in ratio anomaly.
  • wp_cache_hit_ratio sudden drop → cache plugin misconfigured or a new plugin setting per-visitor cookies; usually shows up as the leading indicator of cause (3).
  • wp.state_change type=plugin_activated followed by performance degradation → silent plugin activation correlating to cache or session changes.
  • wp_request_peak_memory_mb crossing memory_limit → PHP-FPM exhaustion, often the downstream consequence of any logged-in flood once it lasts more than 5 minutes.
  • wp.heartbeat_burst → abnormal volume of admin-ajax.php?action=heartbeat calls, frequently the dominant subset of a session-flood waveform.

See what's actually happening in your WordPress system

Connect your site. Logystera starts monitoring within minutes.

Logystera Logystera
Monitoring for WordPress and Drupal sites. Install a plugin or module to catch silent failures — cron stalls, failed emails, login attacks, PHP errors — before users report them.
Company
Copyright © 2026 Logystera. All rights reserved.