JWT Generator

Build a JSON Web Token and sign it with HMAC — HS256, HS384 or HS512 — without leaving the page. Edit the header and payload as JSON, drop in the registered claims with one click, pick an expiry in plain language, and get a finished token you can paste straight into an Authorization header. Signing runs in your browser through the Web Crypto API.

Read this first. The secret you type below is used only inside this page: it is imported with crypto.subtle.importKey and never sent anywhere, and pressing Generate issues no network request at all. Even so — do not paste a real production signing secret into any web page, including this one. Tokens produced here are for testing only. Use a throwaway secret, and mint real tokens from your own auth service.

Header JSON

Payload JSON

Add a claim:

The alg field in the header is rewritten to match the algorithm you select, so the token cannot claim one algorithm while being signed with another. Numeric date claims are seconds since the Unix epoch — the epoch converter turns them back into timestamps.

No token generated yet.
Press Generate & sign and the token will appear here.

What this generator does, and what it deliberately does not

A JWT generator assembles three pieces and joins them with dots. The first piece is a JSON header naming the algorithm. The second is a JSON payload of claims. Both are serialised to UTF-8 bytes and encoded as base64url. The third piece is a signature computed over the exact string header.payload using a key, and encoded the same way. That is the entire construction — there is no hidden step, and once you have seen it written out, the format stops being mysterious.

This page performs the signing with the browser's own crypto.subtle implementation rather than a hand-rolled HMAC. The secret string is encoded to bytes, imported as a raw HMAC key bound to the matching hash function, and passed to crypto.subtle.sign. The resulting ArrayBuffer is base64url-encoded and appended. A token produced here will verify against any correct library — Node's crypto.createHmac, PyJWT, jjwt, golang-jwt — given the same secret and algorithm.

What it does not do is asymmetric signing. This tool is HMAC-only: HS256, HS384 and HS512. RS256 and ES256 require a private key, and inviting anyone to paste a private key into a web form is a bad pattern to normalise even when the implementation is honest. If you need an RS256 token, generate it where the key already lives — your auth server, a CLI, or a short script — rather than in a browser tab. If a page offers to sign with your private key, ask yourself what would happen if that page changed tomorrow.

Why "for testing only" is not boilerplate here

Anything that signs tokens is, by construction, a key-handling tool, and key-handling tools deserve blunt warnings. The signing secret is the whole of your security model for an HMAC token: whoever holds it can mint a token asserting any user ID, any role, any tenant, and every verifier in your fleet will accept it. That is not a downgrade in trust, it is a total bypass.

So the rule is simple. Use a throwaway secret on this page. If you want to test that your middleware accepts a valid token and rejects an expired one, point your test environment at a secret that exists only for testing, generate tokens against it here, and keep the production secret somewhere no browser ever sees. If you have already pasted a production secret into any online tool — this one or another — rotate it. Rotation is cheap; the alternative is trusting a promise you cannot audit.

The inverse operation has no such caveat. Reading a token requires no key at all, which is why the JWT decoder can be used freely on any token you like: it decodes the header and payload and tells you what every claim means, without ever asking for a secret.

Base64url, and the encoding mistakes that break tokens

JWT segments are not Base64. They are base64url, the URL-safe alphabet from RFC 4648 section 5, with padding removed. Three differences matter, and each one produces a distinct class of bug.

  • + becomes -. Standard Base64's plus sign means "space" when a value is form-encoded, so a token carrying one can arrive at the server with a space in the middle of the signature and fail verification for reasons that look like a key mismatch.
  • / becomes _. A slash inside a token makes it impossible to embed the value in a URL path segment without escaping.
  • Trailing = padding is stripped entirely. Padding characters are also percent-encoded in query strings, so leaving them in produces a token that survives a header but breaks in a link.

The encoder on this page applies all three transformations. If you build tokens yourself and find that one library accepts them while another rejects them, a stray = is the first thing to check — some parsers are forgiving, and the forgiving ones hide the problem until you meet a strict one. You can inspect any segment by hand with the Base64 encoder and decoder in URL-safe mode.

One further subtlety catches people who assemble tokens in a shell script: the signature covers the encoded string, not the JSON. If you pretty-print the payload after signing, or re-serialise it with different key ordering or different whitespace, the bytes change and the signature no longer matches. A JWT's middle segment must be reproduced byte-for-byte, which is why libraries hand you the finished string rather than a structure you can edit. Never edit a token by hand and expect it to verify.

The registered claims, and what a verifier does with them

The claim names in the payload are mostly conventions, but seven of them are registered in RFC 7519 and carry defined meanings that libraries act on automatically. The quick-add buttons above insert each one with a sensible starting value.

ClaimNameTypeWhat a verifier does
issIssuerStringCompares it to the expected issuer; a mismatch means the token came from somewhere else
subSubjectStringReads it as the user or principal the token is about
audAudienceString or arrayRejects the token unless this service is named — the check that stops a token for one API being replayed at another
expExpiration timeNumeric dateRejects the token once the current time passes it, usually with a small skew allowance
nbfNot beforeNumeric dateRejects the token until this moment arrives
iatIssued atNumeric dateRecords issuance; some services reject tokens older than a policy maximum regardless of exp
jtiJWT IDStringUniquely names the token so a denylist can revoke exactly one of them

All three time claims are numeric dates: the number of seconds — not milliseconds — since 1970-01-01T00:00:00Z. Passing milliseconds is the single most common JWT bug in JavaScript codebases, because Date.now() returns milliseconds and the resulting token appears to expire roughly fifty thousand years from now. The expiry presets above do the arithmetic for you; if you are computing it yourself, the value should be about 1.7 billion, not 1.7 trillion. Feed either into the epoch converter and the mistake is obvious in a second.

For jti, a random UUID is the conventional choice — generate a batch with the UUID generator if you need identifiers that will not repeat across test runs. The claim only earns its keep if something actually records it: a denylist keyed on jti is how you revoke a single leaked token without rotating the signing key and invalidating everyone else's.

Choosing an expiry

A signed JWT is valid because the maths says so, not because a server says so. That is its great advantage — any service holding the key can verify it without a database round trip — and it is also the reason the expiry deserves more thought than it usually gets. Once issued, a token cannot be withdrawn without adding exactly the shared state that stateless verification was meant to avoid.

The standard arrangement is a short-lived access token and a long-lived refresh token. The access token carries the claims, goes in the Authorization header on every request, and expires in minutes. The refresh token is opaque, stored server-side, revocable, and exchanged for a new access token when the old one lapses. Revocation then means deleting one row, and the blast radius of a leaked access token is bounded by its lifetime. The OAuth vs JWT guide walks through where each piece fits.

Two practical notes. First, allow a little clock skew — a minute is typical — on both exp and nbf, or requests will fail intermittently on machines whose clocks drift. Second, test the failure path deliberately: the "already expired" preset above produces a token with an exp in the past, which is the fastest way to confirm your middleware answers 401 Unauthorized rather than 403 Forbidden or, worse, a 500 from an unhandled exception in the parsing library.

Testing an endpoint with a generated token

The point of generating a token is usually to send it somewhere. Copy the finished value, open the API tester, choose the Auth tab, select Bearer token and paste it in — the request goes out with Authorization: Bearer <token>, which is what almost every JWT-protected API expects. The bearer token guide covers the header's exact syntax and the mistakes that produce a silent 401, such as an extra space or the word "Bearer" duplicated.

A test matrix worth running against any endpoint you protect with JWTs:

  • Valid token — expect a 2xx and the right identity in the response.
  • Expired token — expect 401, not 403, and not a stack trace.
  • Token signed with the wrong secret — change one character of the secret here and regenerate. Expect 401. If it passes, your verifier is not checking the signature at all, which is a surprisingly common misconfiguration in hand-rolled middleware.
  • Token with the wrong aud — expect rejection. If it passes, a token minted for a different service in your estate will work here too.
  • Token with nbf in the future — expect rejection until that moment.
  • No token at all — expect 401 with a WWW-Authenticate header, as the HTTP headers reference describes.

Every one of those cases can be produced from this page in a few seconds, which is the argument for having a generator at hand rather than editing fixtures by hand. Pair it with the API authentication guide if you are choosing between JWTs, session cookies and API keys in the first place.

Secret strength, and the attack that makes it matter

An HMAC secret is not a password, and the intuitions people carry over from passwords are wrong in a dangerous direction. A password is checked by a server that can rate-limit attempts. An HMAC secret is checked by anyone holding a single token, offline, as fast as their hardware allows: guess a candidate, recompute the signature over the token's own first two segments, compare. There is no lockout, no delay and no log entry. A dictionary word or a short phrase falls in seconds on a laptop.

Size the secret to the hash: 32 random bytes for HS256, 48 for HS384, 64 for HS512. Generate it from a cryptographic source — the Random secret button above uses crypto.getRandomValues, and for real key material a command such as openssl rand -base64 48 run on your own machine is the right habit. Never derive a JWT secret from an application name, an environment name or anything that appears in your repository.

Two related failure modes are worth naming because both have caused real breaches. The first is alg: none, a header claiming the token is unsigned; a verifier that honours it accepts anything. The second is RS256-to-HS256 confusion, where an attacker takes a service's public key, signs a token with it as though it were an HMAC secret, and a verifier that reads the algorithm from the token rather than from its own configuration accepts it. The fix for both is the same and is not optional: the verifier decides the algorithm, never the token. The JWT decoder flags alg: none explicitly for this reason.

Finally, remember what the signature does not do. It proves the payload has not been modified. It does not conceal it. Every claim in a token you generate here is readable by anyone who holds the token, with no key and no effort — so a JWT payload is the wrong place for a password, a card number, a full date of birth, or an internal note about the user. Put an identifier in the token and keep the sensitive data behind an authenticated lookup.

Frequently asked questions

Does my signing secret leave the browser?

No. It is imported with crypto.subtle.importKey and used to sign in the page; pressing Generate issues no network request, which you can confirm in the network tab. Even so, do not paste a production signing secret into this page or any other web page.

Can I use a token from this page in production?

No. These are test tokens. A production token should come from your identity provider or auth service, which owns key rotation, audience restriction and revocation. Treat what you generate here the way you would treat a test fixture.

Why does this generator only support HMAC algorithms?

HS256, HS384 and HS512 need only a shared secret. RS256 and ES256 need a private key, and a page that invites you to paste a private key into a text box is a bad pattern regardless of how carefully it is written. Sign asymmetric tokens where the key already lives.

What is base64url and why does the token have no equals signs?

JWT segments use the URL-safe Base64 alphabet from RFC 4648 section 5: + becomes -, / becomes _, and the trailing = padding is dropped. A token containing any of those three characters was encoded with standard Base64 and strict parsers will reject it.

How long should a JWT be valid for?

As short as your refresh flow tolerates. The expiry is effectively your only revocation mechanism, so five to sixty minutes for an access token, paired with a longer-lived refresh token held server-side, is the usual answer.

What is the difference between exp, nbf and iat?

All three are numeric dates in seconds since the Unix epoch. exp is when the token stops being valid, nbf is when it starts, and iat is when it was issued. Passing milliseconds instead of seconds is the classic JavaScript mistake.

How long should the HMAC secret be?

At least the size of the hash output — 32 bytes for HS256, 48 for HS384, 64 for HS512 — and drawn from a cryptographic random source rather than chosen as a phrase. A weak HMAC secret can be recovered offline from one captured token, with no rate limiting to slow the attempt.

Can I decode a token this page produced?

Yes, with the JWT decoder, which reads the header and payload and explains each registered claim. Decoding needs no secret whatsoever.

Is the payload of a generated token encrypted?

No. The signature proves the payload was not altered; it does not hide it. Anyone holding the token can read every claim without a key, so never put a secret inside one.

Related tools and reference