π JWT Encoder
Build and sign a JSON Web Token with HS256 for local testing β everything happens in your browser via the Web Crypto API, nothing is sent anywhere. For testing only β don't paste real production secrets into any web page, this one included.
What gets signed
Your header and payload are parsed as JSON, re-serialised compactly and
Base64URL-encoded with the = padding stripped. Those two strings,
joined by a dot, are the signing input. Web Crypto imports the secret as raw
UTF-8 bytes for HMAC-SHA-256 and signs that string; the 32-byte result becomes
the 43-character third segment.
signature = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)
token = header . payload . signature
With the defaults here - secret test-secret, iat
1700000000, which is 2023-11-14T22:13:20Z - the token ends
Zqg7Fwu9OuZLhOkGDCZ67akZLCn3NsGJwA0pQPPwla4. Change one character of
the secret and every character of that segment changes.
Using it safely
- The header's alg is overwritten with HS256. Typing RS256 or none changes nothing; this page does symmetric HMAC only.
- No claims are added for you - no
iat, noexp. Addexpyourself, in Unix seconds, if the token should expire. - Bad JSON or an empty secret stops the run and the parser's own message is shown.
Frequently asked questions
Is it safe to sign a JWT in the browser?
For test tokens, yes - signing runs locally through Web Crypto and no request leaves the page. For production, no: the secret would have to be shipped to every visitor, and whoever holds it can mint tokens for any user.
Why is my alg still HS256 after I changed it?
The script sets alg to HS256 on the header object just before encoding, because HMAC-SHA-256 is the only algorithm implemented here. RS256 and ES256 need a private key rather than a shared string.
How long should an HS256 secret be?
RFC 7518 requires at least 256 bits - 32 bytes - for HS256. Short words like the default here can be brute-forced offline in seconds against any token you hand out.