Quick answer
A JWT regex is ^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$: three base64url segments (header, payload, signature) joined by dots, where the signature may be empty for unsigned tokens. It confirms the shape only. To trust a token you must verify its signature with the right key and check claims such as exp, aud and iss using a JWT library.
- Tested examples
- Token-by-token breakdown
- Code in 5 languages
- Live tester
How to use the JSON Web Token regex
- 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
- 2Check the sample lines in Test string: every line the JSON Web Token regex accepts is highlighted, rejected lines stay plain.
- 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
- 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
- 5Validate again on the server: client-side checks give users quick feedback, not security.
How does the JSON Web Token regex work?
Read it left to right. Each piece below matches one part of the input:
^Start of the string: no Bearer prefix or whitespace.[A-Za-z0-9_-]+The header: base64url characters (letters, digits, - and _). Standard base64's + and / are not allowed.\.A literal dot between segments.[A-Za-z0-9_-]+The payload with the claims.\.The second dot.[A-Za-z0-9_-]*The signature. It can be empty for an unsecured token (alg: none), which you should normally reject.$End of the string.
Which strings does the JSON Web Token regex match?
The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.
| Input | Matches? | Why |
|---|---|---|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c | Yes | A signed HS256 token. |
eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjMifQ. | Yes | Unsecured token: empty signature. Matches the shape, but reject alg none. |
a.b.c | Yes | Shape only: any three base64url chunks pass. Decode to be sure. |
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ | No | Only two segments. |
Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.c2ln | No | Strip the Bearer prefix from the Authorization header first. |
eyJhbGci+OiJ9.eyJzdWIi.c2ln | No | + belongs to standard base64, not base64url. |
eyJhbGciOiJIUzI1NiJ9.eyJzdWIi.c2ln.ZXh0cmE | No | Four segments. Encrypted JWE tokens have five. |
Does matching the regex mean the token is valid?
No. The pattern says "this looks like a JWT", which is useful for routing, log redaction and early error messages. A token is only trustworthy after a library has:
- Decoded the header and pinned the algorithm you expect (never accept
alg: none, and never let the token pick between HMAC and RSA). - Verified the signature with your secret or the issuer's public key (JWKS).
- Checked the claims:
exp(not expired),nbf,issandaud.
The rules are in RFC 7519 and RFC 7515. To inspect a token's header and claims locally, paste it into the JWT decoder: decoding needs no key, which is exactly why the payload must never hold secrets.
How do I find JWTs in logs to redact them?
Drop the anchors and look for the typical eyJ start (base64url of {"):
const JWT_IN_TEXT = /\beyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g;
const safe = line.replace(JWT_IN_TEXT, "[redacted jwt]");Requiring eyJ on both the header and payload cuts false positives sharply, since almost every real token starts its JSON objects with {". Run the redaction before logs leave the machine: a leaked token is a working credential until it expires.
How do I use the JSON Web Token regex in JavaScript, Python, Go, C# and PHP?
The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.
JavaScript and TypeScript
const regex = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;
function isValid(value) {
return regex.test(value);
}
console.log(isValid("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"));Python
import re
pattern = re.compile(r"^[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]*\Z")
def is_valid(value: str) -> bool:
return pattern.search(value) is not None
print(is_valid("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"))- JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.
Go
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`^[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]*$`)
fmt.Println(re.MatchString("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"))
}C#
using System;
using System.Text.RegularExpressions;
// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]*\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"));- JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.
PHP
<?php
$pattern = '/^[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]*$/D';
function isValid(string $value, string $pattern): bool
{
return preg_match($pattern, $value) === 1;
}
var_dump(isValid('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c', $pattern));- The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).
Which related regex patterns are useful?
- Regex for UUIDs (Versions 1–8): 8-4-4-4-12 hex layout with a real version digit (1–8) and the standard variant.
- Regex for Strong Passwords: 12+ characters with lowercase, uppercase, a digit and a symbol, built from lookaheads.
- Regex for URL Validation: http and https links with a real domain, optional port, path, query and fragment.
- Regex for Alphanumeric Characters: Letters A–Z and digits only, with a Unicode variant for every script.
Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.
Frequently asked questions
Last updated by the Playxoft engineering team.