JWT decoder
Paste a signed JWT to read its header and payload while keeping the original payload bytes visible. The inspector below decodes Base64URL; it does not verify the signature. Identical readable claims do not establish identical signing input.
Input stays in this page. Use the downloadable test tokens to explore the experiment.
No token loaded.
Header JSON
Payload JSON
Original payload text
Byte record
Signature not verified.
Measured November 3, 2026 using Python 3.12.14 and Node v24.19.0. Four newly constructed, nonsecret HS256 tokens were checked with Python hmac and Node crypto. These are local byte and HMAC comparisons, not online-tool, browser-compatibility or production-authentication tests.
Same claims, different byte records
The escaped fixture contained 38 payload bytes and a 132-character token. Its literal UTF-8 counterpart contained 34 payload bytes and a 127-character token. Python and Node decoded both to the same two claims, including the name Café, but recorded different payload SHA-256 values and different HMAC signatures.
Adding spaces without changing either claim produced a 43-byte payload and a 139-character token. Its signing input grew to 95 bytes, compared with 88 for the escaped fixture and 83 for the UTF-8 fixture. Both runtimes accepted the freshly calculated HMAC for each of these three independently signed controls.
Four controlled tokens
Scroll the table sideways on a narrow screen. A passing HMAC below is an offline fixture result, not a validation result from the inspector.
| Fixture | Payload bytes | Signing-input bytes | Token characters | Python HMAC | Node HMAC | Payload SHA-256 |
|---|---|---|---|---|---|---|
| escaped | 38 | 88 | 132 | PASS | PASS | d18e9c06dcfe33fb10f86ef87fc30e7b008a6e9630d9028083cc53ace982f21a |
| utf8 | 34 | 83 | 127 | PASS | PASS | 9b157eaf50f395c211dfeb030c376064ada9c47a53ca1b2b5f2a2fe3917039c1 |
| spaced | 43 | 95 | 139 | PASS | PASS | 502a1cadc76835248fb13540fdd00eee0132e61869e3346f322a9f1a382265d4 |
| utf8-old-signature | 34 | 83 | 127 | FAIL | FAIL | 9b157eaf50f395c211dfeb030c376064ada9c47a53ca1b2b5f2a2fe3917039c1 |
The negative control reused the escaped fixture’s signature with the UTF-8 fixture’s encoded payload. It remained 127 characters long and decoded to the same claims as the valid UTF-8 control. Its HMAC check failed in Python and Node, although its 34 decoded payload bytes matched that valid control exactly.
The escaped, UTF-8 and spaced payloads had three different SHA-256 values in the saved report. The valid UTF-8 token and its old-signature control shared one payload checksum, but only the newly signed token passed the HMAC check. Payload equality alone therefore did not distinguish those two measured controls.
Why the readable view is not enough
A second local control reserialized all three valid claim objects while keeping each original signature. Compact UTF-8 produced the same 34 payload bytes each time, but only the original UTF-8 signature still matched. Pretty UTF-8 produced the same 43-byte payload each time and none of the three retained signatures matched. This was deliberate payload replacement, not a failure to read JSON.
The general distinction between decoding and signature verification is not new. This comparison adds the downloadable byte representations, exact lengths and paired HMAC outcomes. Inspect the original encoded segments when investigating a mismatch; formatting the decoded JSON is a display operation, not a replacement for the original signed bytes.
Why can the same displayed JSON accompany a failed signature check? In this controlled example, the displayed claims hide differences in the serialized payload or in the signature paired with it. Read the raw payload alongside the formatted view, then verify the original token with the correct key and trusted validation rules.
Reproduce the comparison
Download the reproduction script and run it with Python and Node installed. It creates the four synthetic tokens, records Python results, runs the Node comparison and checks agreement. The public test key is included to make the fixture reproducible; it is not a credential for any service.
python reproduce.py- reproduce.py
- new-local-results.json
- normalization-check.json
- manifest.json
- escaped.jwt
- utf8.jwt
- spaced.jwt
- utf8-old-signature.jwt
- prose-audit.json
Limits and related tools
The experiment checks HMAC-SHA256 with one public test key. It does not validate issuer, audience, expiration, nonce or an application session. A matching HMAC does not by itself make a token acceptable to an application. The page’s interactive inspector performs decoding only and never reports a signature as verified.



