JWT Decoder & Hash Generator

Global tools

A JSON Web Token is three base64url segments separated by dots: a header that says how it was signed, a payload of claims, and a signature. This page reads all three, names each claim and what it means, dates the claims that are moments in time, and - when you give it the secret - checks whether the signature really matches.

Decode a token (and check its signature when you have the secret), or hash some text with SHA-256, SHA-1 and MD5.

Header, payload and signature. The example was signed for the year 2100 so it stays readable in this page.

The example token was signed with "danfio-demo-secret", which is published on purpose - type it in to watch a signature match. A password field is never written into the URL.

Leave it blank for "token-report.txt" - or "hashes.txt" in the hash mode.

Results update as you type. Nothing leaves your device.

A token, decoded

8 claims

Issued by https://danfio.com, for danfio-tools, about user_1024. Expires 2100-01-01 00:00:00 UTC (73 years from now).

Algorithm
HS256
Key id
danfio-demo-2026
Claims
8
Signature
not checked

Header, payload, claims and signature

Header
{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "danfio-demo-2026"
}

Payload
{
  "iss": "https://danfio.com",
  "sub": "user_1024",
  "aud": "danfio-tools",
  "iat": 1767225600,
  "nbf": 1767225600,
  "exp": 4102444800,
  "jti": "8f0c1c0e-6a2b-4e5f-9a17-3d5b7c9e1f20",
  "scope": "tools:read reports:read"
}

Claims
iss      https://danfio.com
         issuer: who created the token
sub      user_1024
         subject: who or what the token is about
aud      danfio-tools
         audience: who the token is for - a server checks this matches itself
iat      1767225600
         issued at: when the token was created
         2026-01-01 00:00:00 UTC (10 months ago)
nbf      1767225600
         not before: the token must be refused before this moment
         2026-01-01 00:00:00 UTC (10 months ago)
exp      4102444800
         expiration: the token must be refused after this moment
         2100-01-01 00:00:00 UTC (73 years from now)
jti      8f0c1c0e-6a2b-4e5f-9a17-3d5b7c9e1f20
         JWT ID: a unique name for this token, so it can be revoked individually
scope    tools:read reports:read
         the permissions granted, in OAuth 2.0 scope syntax

Signature
xTefkG02o4JtCQIgIIC0IVb-h7sIDUkxaUkn1l_r-yA (32 bytes, base64url)
HS256: No secret was given, so the signature was shown but not checked. Paste the secret, or the key in hexadecimal, to find out whether it matches.

The whole reading, ready to paste into a ticket - or use the copy button for all of it.

What the numbers do not say

  • Decoding is not verifying. Everything in a token is readable by anybody holding it - the payload is base64url, not encryption - so the claims are worth nothing until the signature says who wrote them.
  • The signature: No secret was given, so the signature was shown but not checked. Paste the secret, or the key in hexadecimal, to find out whether it matches.
  • NumericDate claims (iat, nbf, exp) are seconds since 1970, not milliseconds. Reading one as milliseconds moves it fifty-six years into the past, which is why the moment is printed next to each of them.
  • A symmetric algorithm means one shared secret signs and verifies. That is fine when the same service does both, and wrong when a token is handed to a third party: any party that can verify it can also mint one.
  • A token is a bearer credential: anybody who has it can use it, which is a reason not to paste a live production token into any page - this one included. This page has no server to send anything to and stores nothing you paste, but the rule is the rule.
The token was read in this browser tab: nothing was sent anywhere and nothing was stored. A live production token is still worth keeping out of any page you do not control.

The same page hashes text with SHA-256, SHA-1 and MD5, and compares a checksum you paste in. Everything runs in the browser: the token is not sent anywhere, which is the only reason pasting one here is defensible at all.

About this jwt decoder & hash generator

Can a token’s claims be read without its key? Always - the first two segments are base64url, which is an encoding and not a lock. This page decodes all three parts, names each claim, dates the ones that are moments in time, and separately checks whether an HS256 signature matches the secret you give it.

The claim names and the NumericDate unit come from RFC 7519, and the signature check follows RFC 7515: whole seconds since 1970, not milliseconds. There is no account and no server in the loop; the token is parsed in the tab, which is the only reason pasting one here is defensible at all.

Decoding and verifying are kept apart on purpose, because reading a token proves nothing about who wrote it. The page flags `alg: none` and a header that carries its own key rather than trusting either. For understanding an expired or rejected token, the split between what it says and whether it is genuine is the whole point.

How this jwt decoder & hash generator works

Decoding a token is not verifying it

The first two segments are base64url, which is an encoding and not a lock: anybody holding a token can read every claim in it, with no key and no effort. The payload is not encrypted, and pretending otherwise is how tokens end up carrying data that should never have been in one.

What the signature adds is origin, not secrecy. It is a value only the issuer could have produced, so a verifier that checks it knows the claims were written by somebody holding the key and have not changed since. This page separates the two readings for that reason: what the token says, and separately whether the signature matches the secret you gave it.

The oldest vulnerability in the family is `alg: none`, a token that declares itself unsigned. A verifier that trusts the header hands out anything anybody asks for, which is why the panel treats that case as a finding rather than decoding it politely. The second is a header that carries its own key (`jwk`, `jku`, `x5u`): a verifier that fetches the key from the token is checking the token against itself.

What the claims mean, and the four that are moments in time

A payload is a JSON object, and the registered claims are one letter each: `iss` is who created it, `sub` is who it is about, `aud` is who it is for, `exp` is when it must be refused, `nbf` is when it becomes valid, `iat` is when it was created, and `jti` is a name for this particular token so it can be revoked. Everything else - `scope`, `roles`, `email`, whatever a service invented - is between the issuer and the application.

The four time claims are NumericDate values: whole seconds since 1970, not milliseconds. That one detail accounts for most of the confusion around tokens - a millisecond reading of an `exp` of 1789650000 puts it in 1970, which looks like an expired token rather than a mistaken unit. Each of them is printed here as a date, with how long ago or how far away it is.

The audience claim is the one that most often explains a rejection. A token issued for `api.example.com` is not valid at `example.com`, whatever the signature says, and a service that skips checking `aud` accepts tokens minted for somebody else.

Hashes: what they are good for, and what they are not

A hash turns any input into a fixed-size value. The same text always produces the same digest, changing one character changes all of it, and the value cannot be turned back into the text. That makes it a fingerprint: a checksum confirms a download arrived intact, and a digest confirms two files are the same file.

SHA-256 is the one to use. SHA-1 and MD5 are both broken for security - deliberate collisions have been published for each - but they survive as non-adversarial checksums: S3 ETags are MD5, Git names objects with SHA-1, and plenty of pipelines compare MD5 values. This page computes all three and says which is which, because "match the value you were given" and "protect the value you are creating" are different jobs.

A hash is also not a signature. Hashing a secret together with a message - `hash(secret + message)` - looks like authentication and is not: the construction can be length-extended, so anybody can append to the message without knowing the secret. HMAC exists to fix that, and it is what this page uses to check an HS256 signature, over the exact bytes that were signed.

Worked examples

Each example below was run through the calculator on this page when the site was built, so the numbers match what you see when you enter the same inputs.

Reading a signed token without the secret

What to do: Decode a JSON Web Token, and check its signature

A token, decoded

8 claims

Issued by https://danfio.com, for danfio-tools, about user_1024. Expires 2100-01-01 00:00:00 UTC (73 years from now).

Algorithm: HS256 · Key id: danfio-demo-2026 · Claims: 8 · Signature: not checked

The claims are all readable - that is the point of listing them - but the signature stays unchecked until a secret is given. The panel says so rather than implying the token is trustworthy.

The same token, with the secret that signed it

What to do: Decode a JSON Web Token, and check its signature · Secret or key (only to check the signature): ••••••••

A signed token, decoded - and the signature matches

8 claims

Issued by https://danfio.com, for danfio-tools, about user_1024. Expires 2100-01-01 00:00:00 UTC (73 years from now).

Algorithm: HS256 · Key id: danfio-demo-2026 · Claims: 8 · Signature: matches

The example token is signed with a published key on purpose, so this is the journey worth taking once: the signature matches, and a single changed character in the payload stops matching.

Hashing a piece of text

What to do: Hash some text (SHA-256, SHA-1 and MD5) · Text to hash: abc

SHA-256, SHA-1 and MD5 of the same text

3 characters, 3 bytes

The three digests are over the same bytes; SHA-256 is the one to use unless you are matching something that already uses an older one.

Bytes in UTF-8: 3 · SHA-256: ba7816bf8f01cfea... · Checksum given: not given

The three digests of the same three bytes. SHA-256 of "abc" is the first example in the specification, so it can be checked against any other implementation.

Comparing a checksum somebody gave you

What to do: Hash some text (SHA-256, SHA-1 and MD5) · Text to hash: abc · Checksum to compare: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

SHA-256, SHA-1 and MD5 of the same text

3 characters, 3 bytes

The three digests are over the same bytes; SHA-256 is the one to use unless you are matching something that already uses an older one.

Bytes in UTF-8: 3 · SHA-256: ba7816bf8f01cfea... · Checksum given: matches SHA256

Paste the value you were handed and the page says which of the three it matches. The comparison is over the whole digest in lowercase, with spaces ignored, which is how a checksum usually arrives.

Frequently asked questions

Is it safe to paste a JWT into a decoder?

The payload is readable by anybody who has the token, so decoding it here exposes nothing that the token itself does not already expose. The token is still a bearer credential, though: anybody holding a live one can use it, so it should not be pasted into a page you do not control. This page has no server to send anything to, the decoding happens in your own browser tab, and nothing you paste is stored or logged - which is the standard every tool on this site is built to.

Does a valid signature mean the token is safe to accept?

No. A signature proves origin and integrity, and it says nothing about whether the claims are appropriate: an expired token can have a perfectly valid signature, a token minted for a different audience can have one, and a token signed by a compromised key can have one. A verifier still has to check `exp`, `nbf` and `aud`, look the key up itself rather than trusting the header, and refuse `alg: none`.

Why does the page not verify RS256 or ES256?

Because verifying them needs the issuer’s public key and RSA or elliptic-curve arithmetic, and this page computes only HMAC-SHA-256. Rather than showing a green tick it cannot justify, it names the algorithm it saw and says the signature was not checked. To verify an RSA or ECDSA token, use a library that knows the algorithm and the issuer’s JWKS document - the signature over the first two segments is shown here so you can paste it there.

What does "the signature does not match" mean?

Either the secret is not the one that signed this token, or the token was altered after signing. The check is over the exact bytes of the first two segments, so changing one character of the payload - a role, a user id, an expiry - makes the recomputed value differ. That is the whole point of the signature, and it is why "I can edit my token and the server still accepts it" is a security incident rather than a feature.

Why does my token look expired when it should be valid?

Usually because a NumericDate claim is being read as milliseconds. `exp`, `nbf`, `iat` and `auth_time` are whole seconds since 1970: reading 1789650000 as milliseconds puts it in January 1970, which looks expired. The other common cause is a clock difference between the issuer and the verifier, which is why most libraries allow a small leeway - thirty seconds or a minute - rather than demanding exact agreement.

Should I use a JWT for a session?

Only deliberately. A token carries its own claims and is verified without a database lookup, which is what makes it fast and what makes it hard to revoke: until it expires, it works. If sessions have to be revocable immediately, either keep the token short-lived and pair it with a refresh token, or keep a server-side session. The `jti` claim exists so an individual token can be named and blocklisted, but that is a database lookup again.

Which hash should I use?

SHA-256 for anything new. SHA-1 and MD5 are broken for security - published collisions exist for both - so they should not be used where somebody could gain from producing two inputs with the same digest. They are still the right answer when you have to match a value produced elsewhere: S3 ETags are MD5, Git object names are SHA-1, and a lot of data pipelines compare MD5 checksums. Matching an old value and protecting a new one are different jobs.

Is hashing a password with SHA-256 good enough?

No, and the reason is speed: SHA-256 is designed to be fast, so a modern graphics card tries billions of guesses a second. Passwords need a deliberately slow, memory-hard function such as argon2, scrypt or bcrypt, with a per-user salt so that two users with the same password do not get the same value. This page hashes text to check a checksum, not to store a credential.

Assumptions and sources

  • Tokens follow RFC 7519 (JWT) and RFC 7515 (JWS), and an HS256 signature is checked with HMAC-SHA-256 as RFC 7518 defines it.
  • Known traps are reported rather than hidden: `alg: none`, keys embedded in the token, and RS256 or ES256 tokens that need the issuer's public key.
  • The token and the secret stay in your browser, and the secret is not written to the address bar or the browser history.

Last reviewed 2026-09-16. Runs locally on your device; keep backups; no financial advice applies. Read the full disclaimer.

Where a figure on this page comes from an official agency — HUD, IRS, FICO or a state body — the site keeps a dated snapshot of the source page in its sources library, and a scheduled job re-checks that snapshot, so the number can be traced back to the document it came from.