EasyDeveloper

JWT

How to Decode a JWT: Read the Header and Payload Without a Secret

Published 2026-08-15 · 8 min read

TL;DR: Decode a JWT by splitting the token on the dots and base64url-decoding the first two parts; the signature stays encoded and is never decodable.

To decode a JWT you need nothing more than the token itself: split it on the dots and base64url-decode the first two parts. The header and payload are never encrypted, so no secret, private key or tool license is required. Decoding answers the question what claims does this token carry, and it is deliberately separate from verifying that the token can be trusted. The token structure itself is defined in the JSON Web Token specification.

What does a decoded JWT look like?

Take a typical HS256 token and split it into its three dot-separated segments. The first decodes to the header, the second to the payload, and the third stays an opaque signature:

// raw token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

// header (part 1)
{ "alg": "HS256", "typ": "JWT" }

// payload (part 2)
{ "sub": "1234567890", "name": "Alice" }

The payload here carries two claims: sub, the subject, and a custom name claim. Every token you decode will follow the same pattern - a small header object, a claims object, and a signature you cannot read back.

How do you decode a JWT in a browser?

The fastest path is the JWT Decoder here on this site. Paste the token and the header and payload are shown as formatted JSON with the exp claim called out, plus a notice about whether the token looks expired. There is nothing to install and the decoding happens in your browser.

You can also decode in a browser console without any library. JavaScript exposes atob for base64, and you add back the padding that base64url strips:

function b64url(s) {
  s = s.replace(/-/g, '+').replace(/_/g, '/');
  while (s.length % 4) s += '=';
  return atob(s);
}
const parts = token.split('.');
console.log(JSON.parse(b64url(parts[1]))); // payload

How do you decode a JWT in Node.js?

Node can do it with the built-in Buffer class, which handles base64url directly in recent versions. A one-liner is enough:

const payload = Buffer.from(token.split('.')[1], 'base64url').toString('utf8');
console.log(JSON.parse(payload));

How do you decode a JWT in Python?

Python decodes with the standard library base64 module. The url-safe decode functions accept the - and _ characters used by JWT, and you add padding before decoding because Python requires it:

import base64, json

seg = token.split('.')[1]
seg += "=" * (-len(seg) % 4)
payload = json.loads(base64.urlsafe_b64decode(seg))
print(payload)

Why can you decode a JWT without a secret?

Because the JWT design signs the header and payload rather than encrypting them. Encoding with base64url is the reverse of hiding: it exists so the token survives URLs, headers and JSON strings, not so it keeps data private. The signature covers integrity, not confidentiality. This means decoding a token is always possible, which is exactly why the payload must never hold passwords, credit card numbers or anything else sensitive.

This is also the difference between decoding and verifying. Decoding reads the claims; verifying recomputes the signature with the expected key and checks exp and audience. A token can be decoded successfully and still be a forgery or an expired token - decoding proves nothing about trust. For the verification step, use the JWT Validator or a library such as jsonwebtoken.

Why does my decoder throw a padding error?

Base64url removes the trailing equals signs that padded base64 uses, so the segment length is often not a multiple of four. Decoders that expect standard base64 reject that. Fix it by re-adding padding to a multiple of four before decoding, as the JavaScript and Python examples above do, or use a library with built-in base64url support.

What if a token has more than two dots?

  • A JWT always has exactly three parts. Four or more segments means the string is not a plain JWT, or whitespace and a trailing dot got mixed in.
  • Some systems wrap a JWT in a prefix such as Bearer plus a space; strip the scheme before splitting.
  • A pasted token may include surrounding quotes or newlines from the source - trim them first.

How do you decode a JWT without a tool?

The command line is enough. On Linux and macOS, base64 -d decodes after converting the URL-safe characters back to standard base64; in a pinch, any of the code snippets above works in an online runner. The JWT Decoder does the same conversion and formats the JSON for you, which is why it is usually faster for one-off inspection.

Frequently Asked Questions

Do you need a secret to decode a JWT?

No. The header and payload are plain base64url, so decoding needs no key of any kind. A secret or public key is only needed to verify the signature, which is a separate step from decoding.

What is the third part of the token?

It is the signature: a value computed by signing the encoded header and payload with an algorithm such as HS256 or RS256. It is meant to be verified, not decoded, so it never turns into readable JSON.

Can you edit a JWT and keep it working?

No. Changing any character in the header or payload invalidates the signature, and a verifying server rejects the token. That is the whole point of the signature - it makes tampering detectable.

Why does a JWT decoder sometimes need padding?

The decoder sees base64url text, which omits the padding equals signs that regular base64 uses. Some libraries add padding before decoding; if your decode fails, pad the string back to a multiple of four characters.

Why would you decode a JWT at all?

To read the claims - who the token is for, what roles it grants and when it expires - and to debug why an API is rejecting a request, for example because exp has passed or aud does not match.