JWTs: decoding is free, verifying is not — and never paste your secret

A JWT is trivially decoded by anyone. Its security comes from signature verification — which a decoder cannot and should not do for you.

A JSON Web Token (JWT) is a string that looks like three base64-ish chunks joined by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkYSJ9.x7g4h4qj3G7...

Three parts: header.payload.signature. And here’s the part that surprises people: the header and payload are not encrypted. Anyone who gets a JWT can decode it and read everything inside. The signature is the only part that protects you — and decoding doesn’t touch it.

Decoding is public by design

Base64url decoding the middle chunk gives you the claims in plain text:

{ "sub": "1234567890", "name": "Ada", "exp": 1754620800, "admin": true }

That’s intentional. JWTs are signed, not encrypted, and many systems deliberately put non-secret data in them. The security model is: the token proves who issued it and that it hasn’t been tampered with — verified via the signature — not that the content is hidden.

This is why a JWT decoder can decode any token with no secret at all. Decoding needs nothing from you beyond the token. It’s also why pasting a JWT into a website is a mild leak: you’re showing someone the claims (which may include your email, user ID, roles), even if you’re not giving them the signature secret.

Verifying is a separate, secret-required operation

To verify a JWT you need the signing key:

  • HS256 (HMAC): a shared secret. Anyone with the secret can both forge and verify.
  • RS256 / ES256 (asymmetric): a public/private key pair. The server signs with the private key; you verify with the public key.

Verification checks two things: the signature is valid, and the claims are still good (exp not passed, iat sane, iss/aud match). A decoder cannot do this for you — it would need your secret, and the entire point of a decoder is that it doesn’t. When a library verifies a token in your backend, it’s not “decoding with extra steps,” it’s a different operation that happens to reuse the same three-part format.

The two rules that keep you safe

Rule 1: Never paste a signing secret or private key into any online tool. A legitimate decoder doesn’t need it. A tool that asks for your HMAC secret “to verify the signature” is asking you to hand over the one value that can forge tokens in your system. The correct flow: decode the token client-side to inspect the claims, and verify the signature in your backend where the secret lives.

Rule 2: Don’t trust claims you didn’t verify. Since anyone can decode a token, a decoded "admin": true means nothing until the signature has been checked by code that holds the right key. Tools that show you decoded claims are showing you what the token says, not what the server accepts.

What to look for in a decoder

A decent decoder shows you the three parts separately, flags the algorithm, and warns on known-bad setups — like an alg: none token (used in classic JWT forgery attacks), or a token that’s already expired. It should also run entirely client-side. DevFormat’s JWT decoder never uploads the token — which matters, because a token you’re inspecting might still be valid, and the claims inside are yours.

The short version

  • Decoding a JWT: public, free, no secret, safe if client-side.
  • Verifying a JWT: requires the signing key, done in your backend, never in a public tool.
  • Pasting your secret anywhere: the one move that actually compromises you.