The first segment of a JWT looks tiny, but alg, typ, and kid carry real security weight. Here's exactly what belongs in a JWT header and why.
The three-part anatomy
A JWT is three base64url-encoded segments joined by dots: `header.payload.signature`. The header is the smallest part but it tells the verifier **how** to check the token, which makes it security-critical in its own right.
What's actually inside
A minimal header is a small JSON object:
- `alg`: the signing algorithm, e.g. `HS256`, `RS256`, `ES256`, or `none` (which you should always reject)
- `typ`: almost always the literal string `JWT`
- `kid`: an optional key identifier, used to select which key to verify against when multiple keys are in rotation
That's usually it. The header is not encrypted — it's base64url-encoded, meaning anyone can decode and read it instantly. Never put secrets in the header.
Why `alg` is dangerous if trusted blindly
The single most cited JWT vulnerability class comes from verifiers that read `alg` from the incoming token and dynamically choose how to verify it. An attacker can change `alg` to `none` and strip the signature, or change an RS256 token to claim `HS256` and sign it with the RSA public key (which is, by design, public) as if it were an HMAC secret. A correct verifier **ignores the token's claimed algorithm for key selection** and instead uses a fixed, explicitly configured algorithm (or small allow-list) set by your own server code.
What `kid` is for
When you rotate signing keys, you need a way for the verifier to know which public key (or secret) corresponds to a given token without guessing. The `kid` header value is an identifier — often a short hash or version number — that maps to a specific key in your key store or JWKS document. It is safe to trust `kid` as a lookup index because, even if an attacker manipulates it, they can only point the verifier at a key **you already control and trust**, not smuggle in an arbitrary key.
Other header fields you might see
- `cty`: content type, rarely used, relevant mostly for nested JWTs
- `x5c` / `x5t`: certificate chain or thumbprint, used in some enterprise/X.509-based setups
- `crit`: lists header parameters that must be understood and processed — a verifier that doesn't recognize a listed extension should reject the token
Decoding a header yourself
Because the header is just base64url JSON, you can inspect it without any library — split the JWT on `.`, take the first segment, base64url-decode it, and parse as JSON. Our decoder tool does exactly this and is a fast way to sanity-check what a library actually produced.
Checklist
- Never let the token dictate the verification algorithm — configure it server-side.
- Reject `alg: none` unconditionally, including in test/dev environments.
- Use `kid` to support multiple valid keys during rotation windows.
- Remember the header is readable by anyone — never put secrets in it.
- Treat unexpected or unrecognized header fields (especially with `crit`) as a reason to reject the token.