You click log out. In one app the credential stops working before the page finishes reloading. In the other it keeps working for another fifteen minutes, and there is nothing you can do about it from the server.

Reading the diagram
Follow the two logouts first, then the two requests that come after them.
Steps 1 and 2, the session app. Logging out is a write. Your server deletes the row that says this session exists. From that instant the credential in the browser points at nothing, and it does not matter how many copies of it are floating around.
Steps 3 and 4, the JWT app. Logging out is not a write. There is no row, because that is the entire design: the token proves itself with a signature, so the server never needed a record of it. All logout can do is tell the browser to forget its copy. Your server did not learn anything.
Steps 5 through 8, the red band. This is the part prose keeps failing to make concrete. One second after logout, the same credential arrives from somewhere else. The session app looks for the row, finds nothing, and answers 401. The JWT app checks the signature, finds it valid and unexpired, and answers 200 OK.
Both apps behaved exactly as designed. Only one of them is still trusting a credential the user tried to cancel.
The note at the bottom. The window closes when the exp claim inside the token passes, and never earlier. That number was chosen when the token was minted, by whoever wrote the auth code. If an AI wrote it and picked seven days because the example it learned from picked seven days, your revocation gap is seven days.
Why this matters more than it sounds
The gap only bites when there is a second copy of the credential, which is exactly the situation logout is for. Someone logs out of a shared laptop. An admin revokes a contractor's access. A user changes their password because they think they were phished.
In each case the person taking the action believes it took effect. With a stateless JWT it did not, and no error told them so.
Password changes are the sharp edge
"Change your password if you think someone has your account" is advice every app gives. On a stateless JWT setup it does not evict the attacker, because their existing token is unaffected by anything that happened to the password. They keep the session until it expires on its own schedule.
Why does logging out not invalidate a JWT?
Because nothing on your server was holding it. A stateless JWT carries its own proof of validity in the signature, and your server verifies that signature rather than looking the token up anywhere. Logging out clears the copy in the browser, which has no effect on any other copy that already exists.
How long does the gap last?
Exactly as long as the exp claim inside the token, because that is the only thing that can end it. Fifteen minutes is a common access-token setting. Longer values show up regularly in generated auth code, and the gap is however long that value says.
Does a refresh token fix this?
It shortens the window, it does not close it. Revoking the refresh token stops new access tokens being issued, capping the damage at the current token's remaining life. That is a genuine improvement and the reason short access-token expiry matters, but the token already in play still works.
What actually revokes a JWT then?
A blocklist your server checks on every request. That means a lookup per request, which is server-side state, which is the thing stateless tokens were picked to avoid. You end up paying the cost of sessions and the complexity of tokens together.
So should I just use server sessions?
For a normal browser-based web app, usually yes. Sessions revoke instantly, and the lookup you were avoiding comes back the moment you add a blocklist anyway. JWTs earn their keep between services, or where the party doing the checking genuinely cannot reach your database.
The source
---
title: "Logging out does not log out a JWT"
---
sequenceDiagram
autonumber
participant U as You, clicking<br/>log out
participant T as Someone with a<br/>stolen copy
participant S as App using<br/>server sessions
participant J as App using<br/>stateless JWTs
U->>S: log out
S-->>U: the session row is deleted
U->>J: log out
J-->>U: nothing to delete.<br/>your browser drops its copy
rect rgb(254, 242, 242)
T->>S: same cookie, one second later
S-->>T: 401. no row left to look up
T->>J: same token, one second later
J-->>T: 200 OK. still signed in
end
Note over U,J: one logout, two outcomes. the JWT keeps working<br/>until the expiry baked into it, often fifteen minutes.
Check How Your Tokens Are Configured
CheckYourVibe reads what your deployed app actually ships, including token lifetimes long enough to make a logout meaningless.