Encoding and security

JWT Decoder and Verifier

Decode any JSON Web Token, see what each claim means and when it expires, verify its signature with a secret or public key, or sign a new one.

  • Free foreverNo trial, no paywall, no usage limits.
  • No sign-upOpen the page and start. We never ask for an email to use a tool.
  • PrivateEverything runs in your browser. Your input is never uploaded or stored on our servers.
  • Works offlineOnce the page has loaded, the tool keeps working without a connection.
Paste a token to read its header and claims and check its signature.Write the header and claims as JSON and sign a new token with a secret or private key.

Runs locally with Web Crypto. Nothing is uploaded.

Treat live tokens like passwords. Everything here runs in your browser, but anyone holding a production token can act as that user until it expires. Prefer test tokens.

Encoded token

Paste a JWT. A "Bearer " prefix, quotes and line breaks are ignored.
Clear

Try a live example

A typical session token signed with a shared secret. The secret is filled in so the signature verifies.Signed with a fresh RSA private key. The matching public key (PEM) is filled in for verification.Signed with a fresh P-256 ECDSA key. The public key is filled in as a JWK.A token whose exp passed two hours ago, to see how expiry is reported.An unsigned token. Anyone can forge these, which is why servers must reject them.
  • Header
  • Payload
  • Signature
Paste a token to decode it. Your token never leaves this page.

Verify signature

Proves the token was signed by whoever holds this secret or private key, and that nobody changed it since. Checked with Web Crypto in your browser.

Paste a token above, then enter its secret or public key here.

Header

Algorithm and token type. Decoded from the first part.
The decoded header appears here.

Payload

The claims. Anyone can read these: a JWT is signed, not encrypted.
The decoded claims appear here, with dates in plain English.

Need a secure web app built around this? Talk to our team

Quick answer

A JWT (JSON Web Token) is a compact token made of three Base64url parts, header, payload and signature, joined by dots. The header and payload are only encoded, not encrypted, so anyone can read them; the signature proves who issued the token and that nobody changed it. In Playxoft's JWT decoder, paste a token to see its claims with readable dates and expiry status, then add the secret or public key to verify the signature. Everything runs in your browser.

  • Color-coded token parts
  • Claims explained in plain English
  • Live expiry status
  • Verify HS, RS, PS, ES and EdDSA
  • PEM, JWK and JWK Set keys
  • Encode and sign tokens
  • Runs locally with Web Crypto

How to use the JWT Decoder

  1. 1Paste a token into Encoded token. A Bearer prefix, quotes and line breaks are ignored, and the three parts are color-coded.
  2. 2Read the decoded Header and Payload, then the Claims table, which explains each claim and shows dates in UTC, local time and relative to now.
  3. 3Check the badges: Valid, Expired or Not valid yet come from the exp and nbf claims compared with your clock.
  4. 4To verify, enter the shared secret for HS256/384/512, or paste the public key as PEM, a JWK or a whole JWK Set for RS, PS, ES and Ed25519 tokens.
  5. 5Switch to Encode and sign to edit the header and payload as JSON and sign a new token with a secret or a freshly generated key pair.

What is a JWT, and is it encrypted?

A JSON Web Token (RFC 7519) is a set of claims, facts such as who the user is and when access ends, packed into a string that is safe to put in a URL or an HTTP header. The common form is a JWS (JSON Web Signature, RFC 7515): three Base64url parts joined by dots.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.bdUV6fzqg02cO5s8BpihQT_xT_PCKCv21ZGrR8WLDAc
└──── header ──────┘ └──────── payload ────────┘ └─────────────── signature ───────────────┘

That token decodes to the header {"alg":"HS256"} and the payload {"sub":"1234567890"}. Paste it into the decoder with the secret playxoft-demo-secret-not-for-prod-01 and the signature verifies; change a single character and it fails.

A JWT is signed, not encrypted. Base64url is an encoding that anyone can reverse, and that is all a decoder does before it checks the signature. The signature only proves that the token came from someone holding the key and that it has not been modified since. So never put passwords, payment details or other secrets in a JWT payload.

If the claims themselves must stay confidential, use an encrypted JWE (RFC 7516). A JWE has five parts instead of three; paste one here and the tool reads its header and tells you which algorithms it uses.

How does JWT signature verification work?

The signature covers the exact characters header.payload: the two Base64url strings and the dot between them. A verifier recomputes it with the key and compares. Change one character of the payload and the check fails, which is why nobody can edit a claim without the key.

A token is only trustworthy after all of these checks pass (RFC 8725, JWT Best Current Practices):

  1. Algorithm: accept only the algorithm you expect for that key. Never let the token's own alg decide.
  2. Signature: verify with the issuer's secret or public key, usually fetched from its JWKS endpoint and picked by kid.
  3. Time: reject the token if now is at or after exp, or before nbf, allowing only a small clock-skew leeway.
  4. Issuer and audience: iss must be the issuer you trust and aud must name your API.
  5. Your own rules: scopes, roles, the token type in typ, or a revocation list keyed by jti.

This tool covers the first three. The badges show the validity window by your clock, and the Verify signature panel runs the cryptography with the browser's Web Crypto API, so the key never leaves the page.

HS256 vs RS256 vs ES256: which algorithm should I use?

AlgorithmKeyWho can verifySignatureGood fit
HS256, HS384, HS512One shared secret of at least 32, 48 or 64 bytesOnly holders of the secret, who can also create tokens32, 48 or 64 bytesOne service that issues and checks its own tokens
RS256, RS384, RS512RSA key pair, 2048 bits or moreAnyone with the public key256 bytes for a 2048-bit keyThe most widely supported choice for OpenID Connect and public APIs
PS256, PS384, PS512The same RSA key pairAnyone with the public keySame as RSRSA with modern PSS padding, when every consumer supports it
ES256, ES384, ES512EC key on P-256, P-384 or P-521Anyone with the public key64, 96 or 132 bytesCompact tokens for mobile, IoT and high-volume APIs
Ed25519 (formerly EdDSA)Ed25519 key pairAnyone with the public key64 bytesNew systems that control both ends

Choose an asymmetric algorithm (RS, PS, ES or Ed25519) as soon as a second service needs to verify tokens. With HMAC, every service that can verify a token can also forge one.

RFC 9864 (2025) deprecates the generic EdDSA alg in favor of the fully specified Ed25519. Many libraries still emit EdDSA, and this tool verifies and signs both.

If you do use HMAC, generate the secret randomly. A short, human-chosen secret like secret123 can be cracked offline from a single captured token, because the attacker can test guesses against the signature at full speed without ever touching your server. The Generate secret button in the encoder creates a random secret of the right length.

ECDSA has one interoperability trap: JOSE requires the raw R||S signature (64 bytes for ES256, 96 for ES384, 132 for ES512), while OpenSSL and Java's SHA256withECDSA produce ASN.1 DER by default (.NET's ECDsa.SignData already returns the raw form). This tool detects DER signatures, verifies them anyway and warns you, because strict libraries will reject them.

What do exp, iat, nbf and the other JWT claims mean?

RFC 7519 registers seven claims. All are optional, but most APIs require exp, iss and aud.

ClaimNameMeaning
issIssuerWho issued the token, usually the auth server's URL
subSubjectThe user or service the token is about
audAudienceWhich API the token is for (a string or an array)
expExpiration timeReject at or after this moment
nbfNot beforeReject before this moment
iatIssued atWhen the token was created
jtiJWT IDA unique ID for replay detection and revocation

The three time claims are NumericDate values: seconds, not milliseconds, since 1970-01-01 UTC. For example 1767225600 is 2026-01-01 00:00:00 UTC. A 13-digit value is almost always the bug of writing Date.now() instead of Math.floor(Date.now() / 1000), and the decoder flags it.

Identity providers add more, such as email, nonce and auth_time from OpenID Connect, or scope, client_id and roles from the JWT access-token profile (RFC 9068). The claims table explains the common ones and marks the rest as custom.

What are the most common JWT security mistakes?

  • Accepting `alg: none`. An unsigned token proves nothing. Anyone can write one, so servers must reject it.
  • Algorithm confusion. If a server lets the token choose alg and verifies with whatever key it has, an attacker can sign an HS256 token using the server's RSA *public* key as the HMAC secret. Pin one algorithm per key.
  • Weak HMAC secrets. Short or guessable secrets fall to offline brute force. Use random bytes at least as long as the hash.
  • Skipping `aud` and `iss` checks. A token your identity provider issued for a different app would otherwise be accepted by yours.
  • Trusting `jku`, `x5u` or an embedded `jwk`. Fetching the verification key from a URL inside the token lets an attacker supply their own key. Use a fixed JWKS URL.
  • Long lifetimes without revocation. A stolen JWT works until it expires. Keep access tokens short-lived and rotate them with refresh tokens, or keep a denylist of revoked jti values.
  • Sensitive data in the payload. Everything in it is readable by anyone who sees the token, including browser extensions and logs.

How do I decode and verify a JWT in code?

Use a maintained library and always pass the expected algorithm, issuer and audience:

// JavaScript / TypeScript (jose)
import { createRemoteJWKSet, jwtVerify } from "jose";
const jwks = createRemoteJWKSet(new URL("https://auth.example.com/.well-known/jwks.json"));
const { payload } = await jwtVerify(token, jwks, {
  algorithms: ["RS256"],
  issuer: "https://auth.example.com",
  audience: "https://api.example.com",
});
# Python (PyJWT)
import jwt
claims = jwt.decode(token, public_key_pem, algorithms=["RS256"],
                    audience="https://api.example.com", issuer="https://auth.example.com")
// Go (github.com/golang-jwt/jwt/v5)
token, err := jwt.Parse(raw, func(t *jwt.Token) (any, error) { return rsaPublicKey, nil },
    jwt.WithValidMethods([]string{"RS256"}),
    jwt.WithIssuer("https://auth.example.com"),
    jwt.WithAudience("https://api.example.com"))
// C# (Microsoft.IdentityModel.JsonWebTokens)
var result = await new JsonWebTokenHandler().ValidateTokenAsync(token, new TokenValidationParameters {
    ValidAlgorithms = new[] { SecurityAlgorithms.RsaSha256 },
    ValidIssuer = "https://auth.example.com",
    ValidAudience = "https://api.example.com",
    IssuerSigningKey = new RsaSecurityKey(rsa),
});
if (!result.IsValid) { /* reject */ }

Decoding without verifying, as jose's decodeJwt or PyJWT's verify_signature: False option do, is fine for debugging or for showing a user's name in a browser app. Never make an access decision on a token you have only decoded.

Frequently asked questions

Nothing you paste leaves your browser: decoding uses plain JavaScript and verification uses the Web Crypto API on your device, with no network requests. A live token is still a credential, though, so prefer test tokens and avoid pasting production tokens or secrets into any website you do not control.
Yes. The header and payload are only Base64url-encoded, so anyone can read them. The secret or public key is needed only to verify the signature, and creating a valid signature needs the secret or the private key.
The usual causes are the wrong key (compare the token's kid with the keys in the JWK Set), a secret stored as Base64 but entered as plain text or the other way round, a token that was cut off or edited while copying, or a key that does not match the algorithm, such as an RSA key for an ES256 token.
JWT is the claims format defined in RFC 7519. A JWS is a signed token with three parts, which is what most people mean by a JWT. A JWE is an encrypted token with five parts whose payload can only be read with the recipient's key.
Prefer an HttpOnly, Secure, SameSite cookie, which scripts cannot read, so an XSS bug cannot steal it; add CSRF protection if it is sent cross-site. localStorage is readable by any script on the page. Many single-page apps keep a short-lived access token in memory and the refresh token in an HttpOnly cookie.
Keep access tokens short-lived, typically minutes rather than days, because a stolen token keeps working until it expires. Pair them with refresh tokens, which the server can revoke, so users stay signed in without long-lived access tokens.
Not on its own: a signed token stays valid until exp. Common approaches are short lifetimes with refresh tokens, a server-side denylist of revoked jti values, and rotating the signing key, which invalidates every token it signed.

Last updated by the Playxoft engineering team.

From the engineers who built this tool

Building something bigger than a quick fix?

Auth, payments, dashboards and security are where web apps get hard. We have shipped them many times. Share your idea and we will map out a realistic plan and estimate, free and with no obligation.

More free tools

See all tools

Base64 Encoder and Decoder

Encode text or files to Base64 and back, with Base64url, data URIs and type-detected downloads.

Open tool

URL Encoder and Decoder

Percent-encode or decode URLs and query values, and inspect every parameter.

Open tool

Hash Generator

MD5, SHA-1, SHA-256, SHA-384 and SHA-512 for text or files, with HMAC and checksum verify.

Open tool

UUID Generator

Generate v4 and v7 UUIDs in bulk, and decode any UUID's version and timestamp.

Open tool

Nanoid Generator

Generate nanoid IDs with any alphabet and length, and check how long until a collision.

Open tool

ULID Generator

Generate sortable ULIDs in bulk, decode their timestamps and convert them to UUIDs.

Open tool