Password Regex
Lookahead password policies explained honestly — 8-plus and strict 12-plus four-class gates, NIST caveats, safe on-device testing.
Pattern variants
Pragmatic
/^(?=.*[A-Za-z])(?=.*\d).{8,}$/
What it matches
Eight or more characters containing at least one letter and one digit.
What it deliberately doesn't
Anything shorter, or all-letter and all-digit strings; it counts composition, not strength.
Strict
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/
What it matches
Twelve or more characters with a lowercase letter, an uppercase letter, a digit and a non-alphanumeric (space counts).
What it deliberately doesn't
Strong passphrases that lack one required class — composition rules and length-first advice pull in different directions.
Verified vectors
| Input | Pragmatic | Strict | Note |
|---|---|---|---|
"Rosetta-Stone#1802" | ✓ matches | ✓ matches | |
"Sp3cial Pass!" | ✓ matches | ✓ matches | spaces count toward length and as symbols here |
"password1" | ✓ matches | ✗ no match | meets pragmatic; strict wants 12+ with upper and a symbol |
"Passw0rd" | ✓ matches | ✗ no match | 8 characters — four short of strict |
"S3cure!Pass" | ✓ matches | ✗ no match | 11 characters — one short of strict |
"ALLUPPER123!" | ✓ matches | ✗ no match | no lowercase letter |
"hunter2" | ✗ no match | ✗ no match | 7 characters |
"correct horse battery staple" | ✗ no match | ✗ no match | long passphrase without a digit — composition rules reject it |
"12345678" | ✗ no match | ✗ no match | no letter |
"abcdefgh" | ✗ no match | ✗ no match | no digit |
Usage by language
JavaScript
Machine-verified/^(?=.*[A-Za-z])(?=.*\d).{8,}$/
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/
Python
Machine-verifiedre.compile(r"^(?=.*[A-Za-z])(?=.*\d).{8,}$")
re.compile(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$")
Java
Syntax-reviewed — not machine-verifiedPattern.compile("^(?=.*[A-Za-z])(?=.*\\d).{8,}$")
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z0-9]).{12,}$")
Go
Syntax-reviewed — not machine-verifiedregexp.MustCompile(`^(?=.*[A-Za-z])(?=.*\d).{8,}$`)
regexp.MustCompile(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$`)
Does not compile in Go — RE2 deliberately supports no lookahead (?=…); enforce length and character classes with separate string checks instead.
C#
Syntax-reviewed — not machine-verifiednew Regex(@"^(?=.*[A-Za-z])(?=.*\d).{8,}$")
new Regex(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$")
PHP
Syntax-reviewed — not machine-verified'/^(?=.*[A-Za-z])(?=.*\\d).{8,}$/'
'/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z0-9]).{12,}$/'
Caveats
- A policy pattern checks composition, not strength — NIST SP 800-63B recommends length over forced character classes.
- Lookaheads make this pattern unportable to Go's RE2 (see the language notes).
- Test with made-up strings like the sample — never with a real password.
Related patterns
Do it offline:$ node -e 'console.log(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/.test(process.argv[1]))' 'Rosetta-Stone#1802'
How to use it
- Adopt the pragmatic gate as a signup floor, and pair it with a breached-password check — which catches password1 where composition cannot.
- Reserve strict for compliance regimes that mandate four classes; note it refuses long passphrases missing one, as the vectors admit.
- Experiment only with invented strings like the preloaded sample — real credentials do not belong in any tool, including this one.
Password rules are lookahead territory: each (?=…) scans the whole string for one required class, then .{8,} enforces length. The pragmatic policy wants eight characters with a letter and a digit; strict wants twelve with all four classes. Both check composition, never strength — NIST's guidance favors length over forced variety, and the vectors show why.
Examples
^(?=.*[A-Za-z])(?=.*\d).{8,}$pattern /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
Rosetta-Stone#1802 ✓ matches
Sp3cial Pass! ✓ matches
correct horse battery staple ✗ no match
12345678 ✗ no matchThe passphrase row is the honest embarrassment of composition rules: twenty-eight characters of real entropy fail for lacking a digit; a spaced string with one numeral coasts through.
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$pragmatic /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
strict /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{12,}$/
S3cure!Pass pragmatic ✓ matches strict ✗ no match
ALLUPPER123! pragmatic ✓ matches strict ✗ no match
Rosetta-Stone#1802 pragmatic ✓ matches strict ✓ matchesEleven characters, then no lowercase: each divergent row misses strict by exactly one requirement, which is how these policies fail in the wild.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Lookaheads do not travel to Go
RE2 guarantees linear-time matching by excluding lookaround, so both variants refuse to compile in Go — the language block prints Go's spelling with an honest does-not-compile note. Enforce length and classes with plain string checks.
Common errors
Invalid regular expression: /^(?<12plus>.{12,})$/: Invalid capture group name- Group names are identifiers and cannot start with a digit, so (?<12plus> fails at its first character. Use a letter-leading name — (?<minTwelve>…) — or skip naming; policy lookaheads never need captures.
FAQ
Why did correct horse battery staple fail the check?
No digit — and that is the strongest argument against composition rules: a four-word passphrase outscores Passw0rd in entropy yet fails here. Where policy allows, prefer a length-first rule and welcome passphrases.
Is it safe to type a real password into the embedded tester?
The embedded tester persists nothing — no history, no stored buffer — but the safe habit is absolute: probe with invented strings shaped like passwords, never credentials you actually use.
Why twelve characters in the strict variant?
Twelve-with-four-classes mirrors enterprise baselines, while NIST SP 800-63B treats length as the primary lever. Adjust the {12,} in the tester — the lookaheads are independent and edit cleanly.