Generate signed JSON Web Tokens with custom headers, payload claims and HMAC-SHA signing. Your secret key never leaves your browser — signing uses the Web Crypto API client-side.
Generate JWTs for local testing, debugging auth flows, or generating signed payloads in scripts. Set custom claims, pick HMAC-SHA256 signing, and copy the token. This generator is for development only — don't use the output as production secrets, and never paste real production keys here.
HS256 (HMAC) is symmetric — same secret to sign and verify. RS256/ES256 are asymmetric — sign with private key, verify with public.typ: JWT, alg as picked. Add kid if your verifier rotates keys.iss (issuer), sub (user ID), aud (audience), exp (expiration — Unix seconds), iat (issued at), nbf (not before). Custom claims go alongside.header.payload.signature). Verify with the matching key in your auth server's JWT library.alg: none in production. Accepting unsigned tokens lets anyone forge any identity. Always whitelist allowed algorithms server-side.exp. Tokens without expiration live forever — a leaked token is permanent compromise. Set short exp (5–60 min) and rotate refresh tokens.JWE (encrypted JWT) for sensitive claims, or store server-side and reference by ID.iss and aud on the server. A token signed for service A can be replayed against service B if the secret is shared and audience isn't checked.JSON Web Tokens are the connective tissue of modern auth. OAuth 2.0 access tokens, OpenID Connect ID tokens, AWS Cognito sessions, Auth0 sessions, Firebase Auth, Cloudflare Zero Trust, and every framework's "auth helper" all hand you JWTs. The spec is small (RFC 7519) and the format is elegant — but the historical CVEs against JWT implementations are extensive, and most of them are caused by misuse rather than spec flaws. This guide walks through the structure, the algorithm choices that matter, the alg=none and key-confusion attacks every junior backend engineer needs to know about, and the rotation patterns that production systems actually use.
A JWT is header.payload.signature, all Base64URL-encoded:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0IiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Decoded:
{ "alg": "HS256", "typ": "JWT" } — algorithm and token type.{ "sub": "1234", "name": "John", "iat": 1516239022 } — the claims.base64url(header) + "." + base64url(payload) with the shared secret.The payload is not encrypted. Anyone with the token can read its contents. The signature only proves the token has not been modified since it was issued.
| Claim | Name | Type | Purpose |
|---|---|---|---|
iss | Issuer | string | Who signed the token. Verifiers should check this. |
sub | Subject | string | The subject the claim is about (typically user ID). |
aud | Audience | string or array | Who the token is for. Reject tokens not addressed to your service. |
exp | Expiration | Unix seconds | Token rejected after this time. Required for any non-trivial use. |
nbf | Not Before | Unix seconds | Token rejected before this time. |
iat | Issued At | Unix seconds | When the token was issued. |
jti | JWT ID | string | Unique identifier for the token (replay protection). |
| Algorithm | Type | Key | When to use |
|---|---|---|---|
| HS256 | HMAC + SHA-256 | Shared secret (≥ 256 bits) | Single service signs and verifies its own tokens. |
| HS384 / HS512 | HMAC + SHA-384 / 512 | ≥ 384 / 512 bits | Same as HS256 with stronger hash. |
| RS256 | RSA + SHA-256 | 2048-bit private key | Issuer signs, multiple consumers verify with the public key. |
| ES256 | ECDSA P-256 + SHA-256 | 256-bit ECC key | Same as RS256, smaller signatures, faster on mobile. |
| EdDSA / Ed25519 | Edwards-curve DSA | 256-bit Ed25519 key | Modern default for new asymmetric setups (RFC 8037). |
| PS256 | RSA-PSS + SHA-256 | RSA key | RSA signatures with probabilistic padding (modern variant). |
| none | Unsigned | None | Never accept. Spec allows it; safe libraries reject by default. |
The fork in the road is symmetric vs asymmetric:
alg: "none" and sends it. Vulnerable libraries skip signature verification because the spec technically allowed unsigned tokens. Defence: hard-code an allow-list of accepted algorithms; reject everything else, especially "none".jku / jwk headers — attacker sets a header that points the server at an attacker-controlled JWKS URL. Defence: never trust jku / jwk / x5u / kid values from the token; resolve keys from a server-side allow-list.aud claim validation per service.Long-lived JWTs are an operational liability. The pattern that scales:
kid header so verifiers know which key to use.const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, secret, {
algorithms: ['HS256'], // explicit allow-list — defends against alg=none
issuer: 'https://issuer.example.com',
audience: 'my-service'
});import jwt
decoded = jwt.decode(
token, secret,
algorithms=['HS256'],
issuer='https://issuer.example.com',
audience='my-service'
)parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
token, err := parser.Parse(tokenStr, func(t *jwt.Token) (any, error) {
return secret, nil
})Algorithm alg = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(alg)
.withIssuer("https://issuer.example.com")
.withAudience("my-service")
.build();
DecodedJWT jwt = verifier.verify(token);use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
let mut v = Validation::new(Algorithm::HS256);
v.set_audience(&["my-service"]);
v.set_issuer(&["https://issuer.example.com"]);
let token = decode::<Claims>(token, &DecodingKey::from_secret(secret), &v)?;use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$decoded = JWT::decode($token, new Key($secret, 'HS256'));JWS (JSON Web Signature) is what most people mean when they say "JWT" — payload is signed but readable. JWE (JSON Web Encryption, RFC 7516) wraps the payload in encrypted form so it cannot be read without the key. Use JWE when:
JWE has 5 parts (header.encrypted-key.iv.ciphertext.tag), is roughly 50% larger than JWS, and adds complexity. Default to JWS unless you have a specific reason for encryption.
httpOnly cookies for browser sessions; use Authorization headers for API clients.jwt.verify(token, secret) without checking iss and aud opens cross-service replay.exp validation should allow ±30–60 seconds of leeway.HS256 with a 12-character secret. The minimum security level requires the secret to be at least as long as the hash output (256 bits = 32 random bytes for HS256).Search results for "jwt generator", "create jwt online", and "sign jwt with secret" surface a mix of single-algorithm web forms and full library playgrounds. Three things actually matter when you pick one: whether the secret/private key is sent to a server (most are), whether asymmetric algorithms (RS256, ES256, EdDSA) are supported (most aren't), and whether the tool round-trips with a decoder for verification testing. Here is how the most-used JWT generators compare in 2026:
| Tool | Browser-only signing | HS family | RS / ES / EdDSA | Decoder pairing | Cost |
|---|---|---|---|---|---|
| FreeDevTool JWT Generator | Yes (Web Crypto) | HS256/384/512 | RS256, ES256, EdDSA | Yes (JWT Decoder) | Free |
| jwt.io debugger | Yes | HS256 only by default | RS256 (manual key paste) | Same page | Free |
| tooltester / token.dev | Mixed (some submit to server) | HS256 | Limited | No | Free, ad-funded |
| jsonwebtoken (npm) | Local install | All HS | All RS / ES / EdDSA | Same package | Free, OSS |
| jose (npm — preferred 2026) | Local install | All HS | All RS / ES / EdDSA / JWE | Same package | Free, OSS |
Pick HS256 (or HS384/HS512 if your verifier expects them), paste your shared secret into the secret field, set the header and payload JSON, and click Generate. The browser's Web Crypto API performs the HMAC-SHA computation locally — your secret never leaves the page, and the resulting token can be pasted directly into Authorization: Bearer <token> headers for local testing. For RS256 / ES256, paste a PEM-encoded private key and the same flow works asymmetrically. Security warning: never paste a production signing secret into any online tool, even client-side ones. Use HS256 generators only for local development tokens, dummy claims for unit tests, or short-lived debugging tokens with a throwaway secret.
HS256, RS256, and ES256 all produce valid JWT signatures, but they use fundamentally different cryptographic primitives.
| Algorithm | Family | Key type | Best for | 2026 recommendation |
|---|---|---|---|---|
| HS256 / HS384 / HS512 | Symmetric HMAC-SHA | Shared secret (32+ bytes) | Single-service tokens, internal APIs | Use HS256 minimum; HS512 if compliance requires |
| RS256 / RS384 / RS512 | Asymmetric RSA-PKCS#1 v1.5 | RSA key pair (2048+ bits) | OIDC, federated APIs (issuer signs, consumer verifies with public key) | Acceptable but slower; prefer ES256 for new systems |
| ES256 / ES384 / ES512 | Asymmetric ECDSA | Elliptic-curve key pair (P-256+) | Modern OIDC, JWS standards, mobile-first systems | Best balance of speed + security in 2026 |
| EdDSA (Ed25519) | Asymmetric Edwards curve | 32-byte key pair | High-throughput services, Curve25519 ecosystems | Fastest; supported by jose, golang-jwt, jjwt |
| none | NO signature | None | NEVER USE | Reject any token with "alg":"none" at parse time |
Decision rule: same service signs and verifies → HS256. Cross-service or public verification → ES256 (preferred) or RS256 (legacy). High-throughput → EdDSA. Never accept none regardless of what the header claims.
Pair the JWT generator with the JWT Decoder for verification, the Hash Generator for HMAC payload preparation, the UUID Generator for the jti claim, and the API Authentication Guide for the broader OAuth2 / OIDC story.
iss, sub, exp, iat), and the Signature (HMAC or RSA signature of the header and payload). JWTs are widely used in OAuth 2.0, OpenID Connect, and stateless API authentication.exp claim is a Unix timestamp (seconds since epoch) indicating when the token expires. To expire in 1 hour, set exp to the current time plus 3600. Common companion claims include iat (issued at — when the token was created) and nbf (not before — the token is invalid before this time). Use the quick buttons above to auto-fill these values.crypto.subtle) for HMAC-SHA signing. Your secret key and payload data never leave your machine — no data is sent to any server. For production systems, always sign tokens on the server and never embed your signing secret in client-side JavaScript code.All tools run in your browser, no signup required, nothing sent to a server.