JWT Encoder & Generator
Create JWT tokens with custom header, payload, and optional secret key.
Important: The Signature This Tool Produces Is Not Real HMAC-SHA256
This needs to be said plainly rather than buried: when you type a secret into the "Secret" field and click "Encode," this tool does not compute a standards-compliant HMAC-SHA256 signature. Under the hood it XORs the message bytes against your secret's bytes (repeating the secret if it's shorter than the message) and base64url-encodes the result. That is not HMAC-SHA256, it is not cryptographically secure, and a token signed here will not verify against a real JWT library (jsonwebtoken, PyJWT, jose, etc.) using the same secret โ the "alg":"HS256" header claims an algorithm the signature doesn't actually implement.
Use this encoder to see the structure of a JWT and to build a header/payload combination quickly โ it's genuinely useful for that. Do not use its output as a real authentication token in any system that verifies signatures, and do not treat the "secret" you type here the way you'd treat a production signing key. For real tokens, sign them server-side with a proper cryptographic library where the HMAC implementation is correct and the secret never passes through a browser tab.
What Actually Happens When You Click "Encode"
1. Header and payload are base64url-encoded independently
Each JSON object is stringified with JSON.stringify and base64url-encoded โ standard base64 with +// swapped for -/_ and padding stripped. If you don't set alg or typ in your header JSON, the tool fills in HS256 and JWT for you โ but as noted above, changing alg to RS256 or anything else has no effect on how the signature is actually computed; the signing step is hardcoded regardless of what the header says.
2. Leaving the secret blank produces an unsigned-looking token
If you don't enter a secret, the third segment is left empty, producing a token like header.payload. with a trailing dot and nothing after it. This mirrors the real-world "alg: none" attack pattern that legitimate JWT libraries are specifically hardened against โ some poorly-configured verifiers historically accepted unsigned tokens as valid, which is why modern JWT libraries reject "none" by default.
3. Key ordering and whitespace change the output
JSON.stringify preserves the key order you typed, and that ordering is part of what gets encoded and (if it were real) signed. Two payloads with identical claims in a different order produce different base64url strings โ this is a normal property of JWTs, not a bug, but it explains why "the same data" can look like "a different token" to someone comparing strings by eye.
Claims Worth Setting Deliberately When Testing
Time-based claims (Unix timestamp, seconds)
iat- when the token was minted; set to the current time so downstream code that checks token "age" behaves realisticallyexp- expiration; a common testing mistake is setting this to a past timestamp by accident when hand-typing, which makes every consumer reject the token immediatelynbf- "not before"; useful for testing that your system correctly rejects a token that isn't active yet
Identity claims
sub- the identifier your backend will look up; match it to a real test user ID in your system, not a placeholderaud/iss- many frameworks reject tokens whose audience or issuer doesn't match a configured value, so these are worth testing deliberately with both correct and incorrect values
Where a Fake-Signature Encoder Is Still Genuinely Useful
Given the signature caveat above, the realistic uses are: learning the mechanical shape of a JWT (three base64url segments joined by dots), quickly hand-building a payload to paste into the JWT Decoder to see how a real verifier would parse specific claim combinations, or generating a well-formed-but-unsigned token to feed into a unit test that only checks parsing logic (not signature verification). If your test needs a token that will actually pass signature verification, generate it with the same library and secret your backend uses โ not with a browser tool that can't hold a real key safely in the first place.
Frequently Asked Questions
Will a token generated here pass verification in my backend?
No, not if your backend does real signature verification. This tool's "signature" is a simple XOR-based construction, not HMAC-SHA256, regardless of what the header's "alg" field says โ it will fail verification against any standards-compliant JWT library using the same secret.
I set "alg" to "RS256" but it still generated something โ what happened?
The header field is stored as text in the token, but the signing routine itself always runs the same XOR-based signing step regardless of the declared algorithm. There is no RSA key pair support here; setting "RS256" only changes what the header claims, not how the token is actually signed.
Is it safe to type a real production secret into this page?
Don't. Even though the field never leaves your browser, typing a real signing secret into any tool outside your key-management workflow is bad practice โ and since the resulting signature isn't real HMAC anyway, there's no benefit to using your actual secret here.
Why does the token have a trailing dot with nothing after it?
That happens when you leave the secret field empty โ the tool produces "header.payload." with an empty third segment rather than a real signature.
Can I add arbitrary custom claims to the payload?
Yes โ anything that's valid JSON in the payload textarea becomes part of the payload segment. Keep in mind the payload is only base64url-encoded, not encrypted, so never put secrets or sensitive personal data in it even for testing.
