The previous article covered how REST API requests inside wp-admin are protected by a combination of cookies and nonces. This time we go one level deeper: what actually happens when you “log in,” and why does entering a password once keep you authenticated across dozens of page loads afterward? We’ll also look at how WordPress’s approach differs from the more common server-side session model.
What happens the moment you click “Log In”
When you submit the wp-admin login form, wp_signon() first checks the submitted password against the hashed password stored in the database — nothing unusual so far. Once authentication succeeds, wp_set_auth_cookie() issues an authentication cookie to the browser. From then on, the browser automatically attaches this cookie to every subsequent request, so you never have to re-enter your password.
Note: HTTP itself has no concept of “state.” The server doesn’t remember, at the time of the next request, who logged in during the previous one. The general term for solving this problem — maintaining a logged-in state on top of a stateless protocol — is “session management.”
What’s actually inside the authentication cookie
WordPress’s authentication cookie (stored under a name like wordpress_logged_in_*) is a pipe-delimited string. Roughly speaking, it consists of four parts: the username, an expiration timestamp, a session token, and an HMAC signature. The important detail here is that the username and expiration are stored in something close to plain text inside the cookie itself.
Note: HMAC (Hash-based Message Authentication Code) is a tamper-detection signature computed with a secret key. The same input and key always produce the same output, but a third party who doesn’t know the key cannot forge a valid signature.
If a user could freely rewrite the contents of that cookie, they could swap in someone else’s username and impersonate them. The HMAC signature at the end prevents exactly that. The server computes this signature using secret strings defined in wp-config.php — AUTH_KEY / AUTH_SALT (and a separate LOGGED_IN_KEY / LOGGED_IN_SALT pair specifically for the login cookie). On every request, the server redoes this same computation and checks whether the result matches the signature in the cookie. Without knowing the secret key, an attacker who tampers with the username or expiration cannot produce a matching signature, so the forgery is caught immediately.
The verification that runs on every single request
Every time a page loads, WordPress runs this check through wp_validate_auth_cookie(). The flow is: split the cookie on the pipe character, confirm the expiration hasn’t passed, recompute the expected HMAC from the username, token, and expiration, and compare it against the signature stored in the cookie. If even one of these checks fails, the request is treated as unauthenticated.
The apparent contradiction: “stateless,” yet “log out everywhere” works
Based on the description so far, WordPress’s authentication scheme looks entirely self-contained — as long as the username, expiration, and signature can be verified, the server doesn’t need to look anything up in a database on every request. This has a real advantage: the server doesn’t need to hold onto a large volume of session data.
And yet, the WordPress admin dashboard has a “log out everywhere” feature. If the cookie were truly self-contained with nothing on the server side, there would be no way to invalidate an already-issued, cryptographically valid cookie from the outside. The piece that resolves this is the “session token” embedded in the cookie. WordPress separately maintains, per user, a list of currently valid session tokens as user metadata in the database — handled by a class called WP_Session_Tokens. When validating the authentication cookie, WordPress checks not only that the HMAC signature matches, but also that the token inside the cookie still appears in that user’s list of valid tokens. Triggering “log out everywhere” clears that list of tokens server-side; even though the signature itself would still verify correctly, the associated token can no longer be found, so all subsequent requests using that cookie are treated as expired.
In other words, WordPress’s authentication design is a hybrid of a purely self-contained token and a server-managed, revocable session record. The signature check happens fast and self-contained, while only the part that genuinely needs revocation — the token list — depends on server-side state, getting the benefits of both approaches.
How this compares to the traditional server-side session model
Many web applications take the opposite approach. The cookie handed to the browser contains nothing meaningful at all — just a random session ID — while the actual data (username, permissions, and so on) lives entirely in a server-side lookup table mapping session IDs to user information, held in memory or a database. This means the server must look up that table on every request, but revoking a session is as simple as deleting a single entry from it.
It helps to think of WordPress’s authentication cookie as sitting between these two designs. Lightweight data like the username and expiration travels inside the signed cookie itself, keeping verification cheap, while only the part that actually needs revocation logic — which tokens are currently valid — is carved out into server-side user metadata.
Summary
A wp-admin login state is re-verified on every request through an authentication cookie made up of four parts: username, expiration, token, and HMAC signature. Looking only at the signature check, it resembles a fully self-contained token scheme, but it’s actually a hybrid combined with a list of valid session tokens stored in user metadata — which is precisely what makes a server-initiated action like “log out everywhere” possible. The nonce verification covered in the previous article on REST API authentication also depends on this login cookie working correctly in the first place; both mechanisms ultimately rest on the same underlying authentication layer.