How to Rotate JWT Signing Keys Without Breaking Every Active Session

Wait 5 sec.

Rotate a JWT signing key the naive way, swap it out and redeploy, and every token issued under the old key stops verifying the instant you deploy. Every logged-in user gets kicked out at once. This is one of those problems that's obvious in hindsight and easy to miss the first time you actually need to do it, key rotation isn't something most JWT tutorials cover, because it only becomes a problem well after the initial implementation is working.Here's how to do it without a mass logout.Why keys need to rotate at allScheduled rotation is basic security hygiene, the same reason you rotate database credentials or API keys periodically, limiting how long any single compromised key stays useful. Emergency rotation is the other case: a key leaked, and you need it dead as fast as possible, which is a different, more urgent version of the same problem.The trick: rotate by adding a key, not replacing oneThe naive mental model is "swap old key for new key." The correct one is "add a new key, support both for a while, then remove the old one." This works because a JWT header includes a kid (key ID) claim identifying which key signed it:json{ "alg": "RS256", "kid": "2026-08-key-2", "typ": "JWT"}Your verification code doesn't need to guess which key to check against. It reads kid, looks that key up, and verifies against the matching one. That lookup is exactly what a JWKS endpoint exists for, if you're unfamiliar with how that lookup mechanism works, I wrote a fuller breakdown of JWKS endpoints separately. As long as your JWKS response contains both the old and new public keys during the transition, tokens signed under either one keep verifying, no mass logout.The rotation sequenceGenerate a new key pair. New kid, new private key kept secret, new public key ready to publish.Add the new public key to your JWKS response, alongside the old one. Don't remove the old key yet, this is the step people skip and then wonder why half their users got logged out.Start signing new tokens with the new private key. Existing, already-issued tokens are still signed with the old key and still verify fine, because the old public key is still published.Wait out the old tokens' natural expiry. If your access tokens live for 15 minutes and refresh tokens for 30 days, the old key needs to stay in your JWKS for at least as long as your longest-lived token type that might still be out there, otherwise you'll invalidate legitimate, unexpired tokens.Remove the old public key from JWKS once you're confident nothing still references it. Only now is the rotation actually complete.Why this is harder with HS256 than RS256This entire pattern relies on having a public key to publish separately from the private signing key, which is exactly what RS256 gives you and HS256 doesn't. With HS256, the same secret signs and verifies, so "rotating" means coordinating a secret change across every single service that verifies your tokens, simultaneously, with no JWKS-style transition period to fall back on. If you're choosing between the two for a system where key rotation is a real operational concern, this is a concrete practical factor worth weighing alongside the rest of the tradeoff, I go through the full comparison in HS256 vs RS256.Emergency rotation is the same idea, compressedIf a key is actively compromised, you don't get the luxury of a slow, expiry-based transition. Add the new key immediately, start signing new tokens with it, and revoke trust in the old key right away rather than waiting for its tokens to expire naturally, accepting that this will force-expire some legitimate sessions. That's the tradeoff: a scheduled rotation optimizes for zero disruption, an emergency rotation optimizes for speed, and you can't have both when the key is actually burned.Mistakes worth checking your own setup againstRemoving the old key from JWKS too early, before tokens signed under it have actually expiredCaching your JWKS response for longer than your rotation transition window, so some services keep checking against a stale key listTesting verification against only one kid during development, and never actually testing the multi-key transition state before it happens in productionNo documented emergency rotation runbook, meaning the first time anyone thinks through the emergency process is during an actual incidentHas anyone here had to do this under real pressure, an actual leaked key, not a scheduled rotation? Curious how tight the timeline ended up being in practice.