JWT Encoder

The secret key and claims below never leave your browser — signing happens entirely client-side.

The single source of truth for signing — overwrites the header JSON's own alg key to match, so the two can never drift apart.

Ignored and disabled when the algorithm is "none".

Its alg key is overwritten by the selector above when signing.

Max 10 KB combined · Signs automatically as you type

Signed token

Processed entirely in your browser — nothing is sent to a server.

A JSON Web Token (JWT) is a compact, signed string used to carry claims — statements about a user or session — between a client and a server. Encoding one means building the header and payload as JSON, then producing the signature that makes the token trustworthy to whoever verifies it.

The process is the reverse of decoding. The header (typically {"alg":"HS256","typ":"JWT"}) and payload (your claims — things like sub, name, or exp) are each serialized to JSON, then base64url-encoded — a URL-safe variant of Base64 that swaps +// for -/_ and drops padding. The two encoded segments are joined with a dot to form the “signing input.” For HS256, that signing input is run through HMAC-SHA256 with your secret key, and the resulting signature is itself base64url-encoded and appended as a third segment, giving the familiar three-part header.payload.signature shape. For the unsecured none algorithm, signing is skipped entirely and the token is just the two-part header.payload, with no signature segment at all.

This tool’s algorithm selector is the single source of truth for what actually gets signed — it overwrites the header JSON’s own alg key at signing time, so the two can never quietly disagree with each other even if you type a different value into the header field by hand.

All of this runs through the browser’s native Web Crypto API (SubtleCrypto.importKey and SubtleCrypto.sign), the same primitive real servers use, rather than a third-party JavaScript JWT library. That keeps the signing math itself standard while keeping the tool’s own code small enough to audit at a glance — no dependency to trust beyond the browser.

Only HS256 and the unsecured none algorithm are supported. RS256 and ES256 use asymmetric key pairs and require importing a PEM-formatted private key, which is a meaningfully larger scope than a first version needs — see the FAQ for the full reasoning. A token signed here can be pasted straight into the JWT Decoder to confirm exactly what it contains before you use it anywhere.

Examples

Frequently asked questions

Why does the secret key never leave my browser?
Signing happens with the Web Crypto API's SubtleCrypto interface, which runs entirely inside the page. The secret is only ever held in memory as a form field value and passed directly into crypto.subtle.importKey() — there's no network call in the signing path, and the page's Content Security Policy restricts connect-src to a small, explicitly approved list that couldn't carry it anywhere even if the code tried.
Why does this tool only support HS256?
HS256 is symmetric — signing and verifying both use the same plain-text secret, so it needs nothing more than that secret and native HMAC support to implement correctly. RS256 and ES256 are asymmetric: they sign with a private key supplied as a PEM file, which means parsing PEM/DER key formats and importing SPKI or PKCS8 key material before SubtleCrypto can touch it. That's a meaningfully larger, more error-prone scope than this first version covers — support may be added later, but it's a deliberate v1 limit, not an oversight.
What does the "none" algorithm mean, and what's the danger if I use it?
A JWT with "alg": "none" is unsecured — it has only a header and payload, no signature at all, and anyone can construct one with arbitrary claims. It exists in RFC 7519 for narrow cases like an already-trusted internal pipeline. The real danger is on the verifying side: some JWT libraries, if misconfigured, will accept an incoming alg: none token as if it were validly signed. This tool generates one so you can test exactly that — confirm your own API actually rejects it — not as something to use for a real session token.
What happens if my header or payload JSON is invalid?
Signing stops and an error names which field is the problem — header, payload, or both, if both fail to parse. Nothing partial gets signed; you'll see the exact same JSON your editor would reject, not a guess at what you meant.
Why won't this tool accept a private key or a PEM file?
Only HS256 (a plain-text shared secret) and "none" (no key at all) are supported in this version — see the HS256-only question above. A private-key upload path is out of scope for now specifically because RS256/ES256 support isn't implemented yet; there's nothing here for a PEM file to do.