Every JWT you verify is only as trustworthy as the key set it is verified against. Enter a JWKS URL, an OpenID configuration, or just the issuer domain — the checker fetches what is actually being served and reports what it finds: key types and sizes, rotation readiness, duplicate identifiers, expired certificates, and anything published there that should never have left your server.
This check runs on our server: the URL you enter is fetched from ecdsa.com, not from your browser (a browser cannot fetch someone else's JWKS anyway — CORS forbids it). Only public https URLs are accepted, and nothing is stored.
What a JWKS is. A JSON Web Key Set (RFC 7517) is a JSON document with one member, keys, holding the public keys an issuer signs its tokens with. It is deliberately public: anyone verifying a token fetches it over https, finds the key whose kid matches the token header, and checks the signature. That indirection is what makes key rotation possible at all — the issuer publishes a new key, verifiers pick it up on their next refresh, and no secret ever changes hands.
The three things you can paste. A JWKS URL is fetched and read directly. An OpenID Connect discovery document (/.well-known/openid-configuration) is read first, and its jwks_uri is followed to the keys — that path also checks the advertised metadata. A bare domain or issuer path gets /.well-known/openid-configuration appended, per the discovery spec; if nothing is served there, the URL you typed is fetched as-is instead. The report always shows which route was taken and every URL that was retrieved along the way.
Why kid matters more than it looks. With one key and no rotation, kid is dead weight: there is only one key to try. The day you rotate, it becomes the difference between a switchover and an incident. Without kid, a verifier holding two keys has to try both against every token; failures and successes look the same from the outside, you cannot tell which key signed what, and libraries differ in whether they even attempt the second key. Duplicate kids are worse than none at all, because the verifier believes it has an unambiguous answer and picks whichever key it happened to index first. If you need identifiers and have no naming scheme, the RFC 7638 thumbprint — a SHA-256 over the key's required members, shown for every key in the table above — is derived from the key itself and cannot collide by accident.
The algorithm must be pinned by the verifier. The alg member in a JWK, and the algorithm list in a discovery document, are both hints from the issuer. Neither is a security control. The control lives in the verifying application: it must decide, from its own configuration, that tokens from this issuer are ES256 (or RS256, or whatever you chose), and reject everything else before it ever looks at a key. A verifier that reads alg out of the token header and then applies whatever key it has is the classic algorithm-confusion bug: hand it a token with alg: HS256, HMAC-signed using the issuer's public RSA key as the shared secret — a key it publishes in the very JWKS this page reads — and a naive library will verify it happily. The other half of the same defect is alg: none, an unsigned token that some libraries once accepted as valid. Both are catalogued in RFC 8725, the JWT best-practices document, and both are fixed on the verifier's side, not the issuer's.
Private material in a public document. The check this page treats most seriously is the presence of private members: d on an EC or OKP key, p, q, dp, dq, qi on an RSA key, or a symmetric kty: "oct" key whose entire content is the secret. It happens more often than it should, and always the same way: a serialisation call was asked for the key rather than for its public half, and the result was published on a URL the whole internet is invited to read. Anyone who fetches it can sign tokens indistinguishable from yours. There is no partial remedy — the keys are burned, and the fix is to generate new ones, publish only the public members, and roll every consumer over. (This tool redacts such values from the JSON it displays; they remain public at the source, which is the point.)
How the fetch is constrained. Because the request leaves our server rather than your browser, the URL is treated as untrusted input: https only, default port only, and public DNS-shaped hostnames only. The hostname is resolved with a real DNS lookup and every address it returns is screened against the private and reserved ranges — 10/8, 172.16/12, 192.168/16, 127/8, 169.254/16, 100.64/10, multicast, ::1, fc00::/7, fe80::/10 — and the connection is then made to the screened address itself, with the hostname carried in SNI and the Host header so certificate validation still applies. Redirects are never followed automatically: up to three are followed by hand, and only within the same host. Each request gets 8 seconds and the whole chain about 9; responses are capped at 1 MB and must be JSON. Twenty checks per minute per IP address — every one of them is a real request to somebody else's endpoint. (The rate limit is held in the memory of one server instance, so on a platform that runs several it is enforced per instance.)
What this cannot tell you. Everything above is read from what your issuer publishes, which is exactly half the picture. The other half is your verifier: whether it pins the algorithm, whether it checks iss and aud, how long it caches this key set, and what it does when a kid is missing from its cache. A key set with no findings on this page can still sit in front of an application that accepts forged tokens. If you have a token to look at, the JWT debugger decodes and verifies it locally in your browser, and the certificate decoder will take an x5c entry apart in full.