JWT Inspector
Paste a JWT to see its header, claims and expiry, flag risky algorithms, and verify the signature with a public key. Everything runs in your browser.
Where your data goes
| Using this page | Using the API, MCP or A2A | |
|---|---|---|
| Where it runs |
In your browser. The token is decoded, and its signature checked with jose and Web Crypto, in this page; the public key and any HMAC secret you type stay in the tab, and the page makes no network requests (it never fetches JWKS, jku or x5u URLs). | In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller. |
| What leaves your device | Nothing you enter. After the page and its scripts load, no request
carries your input. The browser enforces this: the page's Content-Security-Policy
sets connect-src 'none' and form-action 'none', which
block fetch, XHR, WebSocket, EventSource, beacons and form posts.
| The server receives the token and, if given, a public key. It decodes and verifies them in memory and returns the result; the input is not stored or logged (only the per-call metadata row is recorded). The server makes no network requests (it never fetches |
| Stored | Nothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice. | Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country. |
| Logged | Nothing (no requests are made). | Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies. |
| Access | Open to anyone. | Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare. |
Details and how to verify this yourself: Security & privacy.
User guide
The tool sits in a window that works like a desktop app: menus on the top row and the full-screen icon at its right (Esc leaves full screen). The set-up options described below are in Settings → Options…, an overlay, and the Settings menu also lists the common choices directly. To keep the options in view instead, choose View → Inline config; the page remembers that choice. Help → Keyboard shortcuts… lists the keys.
- Paste the token. A leading
Bearer, surrounding quotes and line breaks are removed for you, with a note. - Read the summary card: the algorithm, issuer, subject, audience, and the three time claims with how long ago or how far ahead they are. Red pills flag an expired token, one not yet valid,
alg: none, an unusual algorithm, or a missingexp. - To verify the signature, paste the issuer’s public key: its JWKS JSON (from the
jwks_uriin its OpenID configuration), a single JWK, aPUBLIC KEYPEM, or the signing certificate. The banner turns green only when the signature verifies.
HMAC tokens (HS256, HS384, HS512) are signed with a shared secret, not a key pair. Open Verify … with a shared secret and type the secret: it is checked in this tab with Web Crypto and never sent anywhere. The API cannot do this (see below).
Decoding is not verifying. Anyone can create a token with any claims. Until the banner says Signature valid with the issuer’s key, treat every claim as untrusted input.
Tokens are credentials. A token that has not expired lets whoever holds it act as its subject. This page decodes it locally; be careful where else you paste it.
Specification
Formats.
- JWS compact serialization (RFC 7515 §7.1): three base64url segments,
header.payload.signature. The header must be a JSON object. If the payload is a JSON object it is the JWT claims set (RFC 7519); otherwise it is shown as text (payloadText). - JWE compact serialization (RFC 7516 §7.1): five segments. Only the protected header is decoded. The content cannot be read without the recipient’s key, which the API never accepts.
- Encoding: segments must be unpadded base64url (RFC 7515 §2). A token containing
+,/or=is rejected with an explanation, because conforming verifiers reject it too. - Not supported: JWS/JWE JSON serialization.
"b64": false(RFC 7797) payloads are shown as text. - Duplicate JSON members: the last one wins (
JSON.parsebehaviour).
Registered claims (RFC 7519 §4.1).
| Claim | Output |
|---|---|
iss, sub, jti | String, or null with a warning if it has another type |
aud | Always an array: a single string becomes a one-item array |
exp, nbf, iat | value (seconds, as given), iso (UTC), and secondsFromAt = value − reference time; negative is in the past |
A NumericDate that is not a JSON number is ignored with a warning. A value above 10¹¹ triggers a warning that it is probably in milliseconds.
Status flags, evaluated at the reference time at (default: now) with clock_tolerance seconds of leeway (default 0):
| Flag | Rule |
|---|---|
expired | at ≥ exp + tolerance. The token is invalid at the exp instant (RFC 7519 §4.1.4) |
notYetValid | at < nbf − tolerance |
issuedInFuture | at < iat − tolerance |
missingExp | The payload is a claims set without exp |
algNone | alg is none (RFC 7518 §3.6): unsecured, anyone can forge it |
weakAlgorithm | alg is none, or names SHA-1 or MD5 (for example RS1, HS1) |
unusualAlgorithm | alg is not registered (including the wrong case, such as hs256), or is ES256K, Ed448 or ML-DSA |
symmetric | HS256/HS384/HS512 |
Other checks reported as warnings.
- Signature length: HS*, ES*, Ed25519 and Ed448 signatures have a fixed size. A wrong length is flagged; for ECDSA this usually means the signature was DER-encoded instead of raw
r‖s(RFC 7518 §3.4). - Key-location headers:
jkuandx5uare never fetched, and an embeddedjwkorx5cis never used to verify. Trusting a key that the token itself supplies is a known attack. critextensions are listed. Unknown critical extensions make verification fail, as RFC 7515 §4.1.11 requires.
Signature verification.
- Key formats: a JWK, a JWKS (
{"keys": [...]}, or a bare array), aPUBLIC KEY(SPKI) PEM, or aCERTIFICATEPEM. Certificate validity, chain and revocation are not checked; only its public key is used. - JWKS key selection: when the token has a
kid, only keys with thatkidare considered. Then a key must have the rightktyand curve for the algorithm,algabsent or equal to the token’s, andusenotenc. Every remaining key is tried, and the first that verifies wins. - Algorithm: the token’s own
algis the only one allowed. jose rejects RSA keys smaller than 2048 bits. - Supported algorithms: RS256/384/512, PS256/384/512, ES256/384/512, EdDSA (Ed25519 keys) and Ed25519. ML-DSA-44/65/87 work only where the runtime’s Web Crypto supports them. ES256K and Ed448 are reported as unverifiable.
- Outcomes:
valid;invalid-signature(the key does not match: the token was altered, or a different key signed it); orunverifiable, with anerrorexplaining why (no key with thatkid, wrong key type,alg: none, HMAC, unsupported algorithm).
Why HMAC tokens cannot be verified through the API. HS256/384/512 use one shared secret both to sign and to verify, so anyone who can verify can also forge tokens. This service never accepts secrets:
keyvalues withkty: "oct"or akmember are refused.- So are private JWKs (
d,p,q,dp,dq,qi,oth,priv) and PEM blocks labelledPRIVATE KEY.
If a token says HS256 but the issuer normally signs with RS256 or ES256, it may be an algorithm-confusion attack: the attacker signs with the public key used as an HMAC secret. Reject it.
No network access. The API and the page never fetch anything, so key cannot be a URL. To verify against an issuer’s keys:
- Fetch
https://<issuer>/.well-known/openid-configurationyourself. - Take its
jwks_uriand fetch that. - Pass the JSON you got as
key.
Limits. API token and key: at most 65,536 characters each.
API & MCP
The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.