JWT Decoder

Free JWT decoder: paste a JSON Web Token to decode its header and payload, see when it expires in plain language, and verify the signature with a secret or public key. Decoding happens in your browser, so the token never leaves your device.

Your token stays on your device. It is decoded and verified by JavaScript in this browser tab. It is never sent to a server, logged or saved, and it's gone when you close the page.

Decoded
Header
—
Payload
—
Signature

Paste a token to check its signature.

Times are shown in your time zone and in UTC. To convert other Unix timestamps, use the Unix timestamp converter; to format other JSON, use the JSON formatter.

How to decode a JWT

  1. Paste the token into the Encoded token box. A Bearer prefix, quotes or line breaks are removed for you.
  2. The header and payload appear as formatted JSON, and the token is shown split into its coloured parts.
  3. The claims table explains each claim and converts exp, iat and nbf to your local time and UTC, with “expires in …” or “expired … ago”.
  4. To verify the signature, enter the secret (HS256/384/512) or paste the public key (RS, PS, ES or EdDSA). The result updates as you type.

Press Sample to try it with a token signed a moment ago, together with its secret.

What's inside a JWT

A JWT is three Base64url strings joined by dots: header.payload.signature. Here is an example token, signed with HS256 and the secret freetoolhq-demo-secret:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNzkwMDAwMDAwLCJleHAiOjE3OTAwMDM2MDB9.CqJxBvfQLOTlYS1lrXmhkTejLMbhSfYn_PILrvB1DVI
  • Header: the signing algorithm and token type: {"alg":"HS256","typ":"JWT"}.
  • Payload: the claims, the data the token carries: {"sub":"1234567890","name":"Jane Doe","iat":1790000000,"exp":1790003600}.
  • Signature: 32 bytes of HMAC-SHA256 over header.payload. Change one character of the header or payload and it no longer matches.

The first two parts are just Base64 in its URL-safe form (with - and _ and no = padding), which is why almost every JWT starts with eyJ: that is {" followed by a letter, encoded. Decoding needs no key. If you need to reformat a payload you copied, the JSON formatter pretty-prints it.

Registered JWT claims

RFC 7519 defines seven standard claim names. None is required, but most access and ID tokens use them:

ClaimMeaning
issIssuer: who created and signed the token
subSubject: the user or entity the token is about
audAudience: who the token is meant for
expExpiration time
nbfNot valid before
iatIssued at
jtiJWT ID: unique identifier, used to prevent replay

Anything else in the payload, such as name, email, roles or scope, is a custom or OpenID Connect claim. The decoder labels the common ones.

JWT expiry: exp, iat and nbf

The time claims are Unix timestamps in seconds (UTC), not milliseconds. In the example token:

  • iat 1790000000 = 2026-09-21T14:13:20Z, when the token was issued.
  • exp 1790003600 = 2026-09-21T15:13:20Z, 1 hour later. Read 30 minutes after issue, the decoder says “Valid, expires in 30 minutes.” Three days after issue it says “Expired 2 days 23 hours ago.”
  • nbf (not before), when present, is the earliest time the token may be used.

Servers usually allow a minute or two of clock skew. A 13-digit value is a millisecond timestamp by mistake; the decoder flags it. To convert any other timestamp, use the Unix timestamp converter.

Decoding vs verifying a JWT

Decoding only reads the token; anyone can do it. Verifying checks the signature with a key and proves two things: the token was issued by someone who has the key, and nobody changed it since. The example token verifies with freetoolhq-demo-secret and fails with any other secret:

  • HS256, HS384, HS512 (HMAC): the issuer and the API share one secret. Enter it in the Secret field. If your secret is stored as Base64, tick Secret is Base64-encoded.
  • RS256, PS256, ES256 and their 384/512 versions: the issuer signs with a private key and anyone can verify with the public key. Paste it as a PEM (-----BEGIN PUBLIC KEY-----) or a JWK, for example one entry from the issuer's JWKS endpoint.

Verification runs in the browser's built-in Web Crypto API (SubtleCrypto), so no crypto library is downloaded and nothing is sent anywhere. A green result means the signature is valid; your server must still check exp, aud, iss and the expected algorithm.

Is it safe to paste a JWT into a website?

Treat a live token like a password: whoever has it can call the API as that user until it expires. Only paste production tokens into a decoder that runs locally. This one decodes and verifies in JavaScript on your device: the token, secret and key are never sent to a server, never saved in your browser's storage and disappear when you close or reload the page. You can check this by opening your browser's developer tools and watching the Network tab while you paste. If a token has leaked somewhere else, revoke it or wait for it to expire.

JWT security checks

  • alg “none”: an unsigned token. Reject it on the server.
  • Algorithm confusion: a server that accepts both HS256 and RS256 can be tricked into using its public key as an HMAC secret. Pin the algorithm you expect.
  • Weak HS256 secrets: short or guessable secrets can be brute-forced offline from any token. Use at least 32 random bytes.
  • Secrets in the payload: the payload is readable by anyone, so keep passwords and personal data out of it.
  • Long lifetimes: prefer short-lived access tokens (minutes to an hour) and refresh tokens.

Decode a JWT in JavaScript and Python

// Browser: read the payload (no verification)
const part = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
const bytes = Uint8Array.from(atob(part), (c) => c.charCodeAt(0));
const payload = JSON.parse(new TextDecoder().decode(bytes));

// Node.js
JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString("utf8"));

# Python (standard library)
import base64, json
part = token.split(".")[1]
json.loads(base64.urlsafe_b64decode(part + "=" * (-len(part) % 4)))

# Python with PyJWT: verifies the signature and expiry
jwt.decode(token, secret, algorithms=["HS256"])

Related developer tools

Frequently asked questions

Can I decode a JWT online?

Yes. Paste the token into the JWT decoder and the header and payload appear as formatted JSON, with the expiry in plain language. Choose a decoder that works in your browser, like this one: a token is a credential, and a decoder that sends it to a server could log it. This page decodes with JavaScript on your device and sends nothing.

Can I decode a JWT without the secret key?

Yes. The header and payload are only Base64url-encoded, not encrypted, so anyone who has the token can read them without a key. The secret (for HS256) or the public key (for RS256 and ES256) is only needed to verify the signature, which proves the token came from the issuer and wasn't changed.

What is JWT and why is it used?

A JSON Web Token (RFC 7519) is a compact, signed set of claims such as a user ID, roles and an expiry time. Servers issue one after login, and the client sends it with each request, usually in an "Authorization: Bearer" header. Because it is signed, an API can trust the claims without a database lookup, which is why JWTs are common in OAuth 2.0, OpenID Connect and single sign-on.

How can I decode a JWT in Python?

Split the token at the dots, add back the = padding and use base64.urlsafe_b64decode on the second part, then json.loads. With the PyJWT library, jwt.decode(token, options={"verify_signature": False}) reads the claims without checking them, and jwt.decode(token, key, algorithms=["HS256"]) verifies the signature and expiry. Only trust claims from the verifying call.

Is a JWT encrypted?

A normal JWT (a JWS, with three parts) is signed, not encrypted: anyone can read its payload. Don't put passwords or other secrets in it. An encrypted JWT (a JWE) has five parts and can only be read with the recipient's key; this decoder tells you when you paste one.

How do I check if a JWT has expired?

Look at the exp claim: it is a Unix timestamp in seconds (UTC). If it is earlier than the current time, the token has expired. The decoder converts exp, iat and nbf to your local time and UTC and says, for example, "expires in 2 hours" or "expired 3 days ago", updating while the page is open.

Why does my token show "Invalid signature"?

Either the secret or key is not the one that signed the token, or the header or payload was changed after signing. Common causes: a trailing space or newline in the secret, a secret that is actually Base64-encoded (tick "Secret is Base64-encoded"), using a key from another environment, or a token copied with part missing.

What does alg "none" mean?

It means the token is unsigned, so anyone can create or edit it. Some early JWT libraries accepted such tokens as valid, which let attackers remove the signature and change the claims. Servers must reject alg "none" unless they deliberately use unsigned tokens, which is why the decoder shows a warning.

Which algorithms can this decoder verify?

HS256, HS384 and HS512 with a shared secret; RS256, RS384 and RS512, PS256, PS384 and PS512, and ES256, ES384 and ES512 with a public key in PEM ("BEGIN PUBLIC KEY") or JWK form; and EdDSA (Ed25519) where the browser supports it. Verification uses the browser's built-in Web Crypto API. Paste public keys only, never a private key.

This tool is provided as is for development and debugging. A valid signature only shows who signed a token; your server must still check the algorithm, issuer, audience and expiry. Spotted a wrong result? Tell us. Last reviewed .