/^/
HardSecurity7 min read

Strong Password Regex

Validates that a password is at least 8 characters long and contains a lowercase letter, an uppercase letter, a digit, and a special character, using lookahead assertions.

#password#security#validation#lookahead#authentication

Regex Pattern

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$

Pattern Breakdown

Hover over a token to see what it does.

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$
TokenMeaning
(?=.*[a-z])Positive lookahead requiring at least one lowercase letter anywhere in the string.
(?=.*[A-Z])Positive lookahead requiring at least one uppercase letter anywhere in the string.
(?=.*\d)Positive lookahead requiring at least one digit anywhere in the string.
(?=.*[!@#$%^&*])Positive lookahead requiring at least one special character from the given set.
[A-Za-z\d!@#$%^&*]{8,}The actual match: 8 or more characters, each drawn from letters, digits, or the allowed special characters.
^Anchors the checks to the start of the string.
$Anchors the match to the end of the string.

Detailed Explanation

What it does

This pattern enforces a common 'strong password' policy: the password must be at least 8 characters, and must contain at least one lowercase letter, one uppercase letter, one digit, and one special character from a fixed set. Every character in the password must also come from an allowed set of letters, digits, and specials.

Why it works

Each `(?=...)` is a zero-width positive lookahead: it checks that a condition holds somewhere ahead in the string without consuming any characters, which is why four lookaheads can be stacked back to back at the same starting position. After all four conditions are confirmed to hold somewhere in the string, the final `[A-Za-z\d!@#$%^&*]{8,}` actually consumes and validates the whole string, ensuring both the composition rules and the character whitelist are satisfied together.

Common use cases

  • Enforcing password complexity rules during account sign-up
  • Client-side validation feedback before submitting a password change form
  • Server-side defense-in-depth validation alongside a password strength meter
  • Generating consistent password policy documentation for a security review

Edge cases

  • A password with all required character classes but using a special character outside the allowed set, like a comma, fails the whitelist even though it 'feels' complex
  • Very long passwords are still accepted since the length quantifier is unbounded ({8,}), which is desirable for passphrases
  • Unicode letters (e.g. accented characters) are not matched by [A-Za-z], so passwords in other scripts may fail even if 'strong'
  • A password consisting only of the four required categories repeated, like 'Aa1!Aa1!', passes even though it is highly predictable

Limitations

  • Does not check against common password dictionaries or known-breached password lists
  • Does not measure actual entropy, so it can approve weak-but-technically-compliant passwords like 'Passw0rd!'
  • The special character set is fixed and does not include every symbol some systems allow (e.g. spaces, brackets, or Unicode symbols)
  • Does not support Unicode letters outside the basic Latin alphabet without modification

Interactive Tester

Edit the pattern or text below — matching runs live in your browser.

Passw0rd! Pw1!abcd

Test Cases

Editable — add your own inputs to see if they pass.

InputExpectedResult
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass

Language Variants

Production-ready examples in 12 languages.

const strongPasswordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/;

function isStrongPassword(value) {
  return strongPasswordPattern.test(value);
}

console.log(isStrongPassword("Passw0rd!")); // true

Common Mistakes

Assuming Go's or Rust's standard regex engines support lookahead the same way JavaScript does.

Fix: RE2-based engines (Go's regexp, Rust's regex crate) don't support lookahead; compose the check from several simple regexes or manual character scans instead.

Forgetting that the character class at the end restricts the entire password, so a special character outside the allowed set causes a full rejection.

Fix: Expand the allowed character class (and the corresponding lookahead) to include every symbol your policy should accept.

Believing a regex-passing password is automatically secure.

Fix: Pair format validation with a breached-password check (e.g. against the Have I Been Pwned range API) and an entropy estimator like zxcvbn.

Performance Notes

  • Each lookahead independently scans up to the full string with `.*`, so four lookaheads mean roughly four linear scans, which is still fast for realistic password lengths.
  • Because lookaheads are zero-width, they don't consume input, so there's no backtracking interaction between them and the final matching group.
  • The unbounded `{8,}` quantifier is safe here since it's a simple character class with no nested or overlapping quantifiers, so there's no catastrophic backtracking risk.

Browser Compatibility

EngineSupportedNotes
ChromeYesLookahead assertions are supported in all modern JavaScript engines.
FirefoxYes
SafariYes
EdgeYes
Node.jsYes