# Regex for Password Validation

> Password regex requiring lowercase, uppercase, digit, and symbol via lookaheads — with the breakdown, a Go/RE2 warning, and why length beats complexity.

```
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/
```

This is the classic complexity check — at least 8 characters with one lowercase, one uppercase, one digit, and one symbol — built from four lookaheads. Each (?=…) is a zero-width assertion: it scans ahead for its requirement without consuming anything, so all four apply to the same string. Worth knowing before you ship it: NIST's current guidance favors length and a breached-password check over composition rules.

## Token by token

| Token | Meaning |
|---|---|
| `(?=.*[a-z])` | lookahead: somewhere ahead there's a lowercase letter |
| `(?=.*[A-Z])` | lookahead: an uppercase letter exists |
| `(?=.*\d)` | lookahead: a digit exists |
| `(?=.*[^A-Za-z0-9])` | lookahead: something that isn't a letter or digit — a symbol or space |
| `.{8,}$` | then actually consume: any 8 or more characters to the end |

## Examples

- ✓ matches: `Str0ng!Pass`
- ✓ matches: `aB3$efgh`
- ✓ matches: `Corr3ct-Horse`
- ✗ does not match: `password`
- ✗ does not match: `SHORT1!`
- ✗ does not match: `NoSymbol1`

## Variations

- `^.{12,128}$` — the NIST-style alternative: just enforce length (with a sane upper bound) and check against breached-password lists in code
- `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$` — drop the symbol requirement, raise the minimum to 10

## Language notes

- Go: this pattern will not compile — RE2 has no lookaheads. Write four separate strings.Contains-style checks instead; it's clearer anyway.
- The same applies to Rust's default regex crate and RE2 bindings everywhere: lookaheads are a backtracking-engine feature (JS, Python, PCRE).

## FAQ

### How do the (?=…) lookaheads work together?

Each lookahead anchors at the start of the string, peeks ahead for its requirement, and gives back control without moving the position. Because none of them consume characters, all four conditions are checked against the entire string, and only then does .{8,} do the real matching.

### Are composition rules actually good password policy?

The evidence says length matters more. NIST SP 800-63B recommends a minimum of 8 (ideally allowing much longer), screening against known-breached passwords, and dropping mandatory composition rules — users satisfy them predictably (Password1!), which attackers know.

## Related patterns

- [Regex for Username Validation](https://www.devkult.com/regex/username.md)
- [Regex for Email Validation](https://www.devkult.com/regex/email.md)
- [Regex for URL Slug Validation](https://www.devkult.com/regex/slug.md)

Open the interactive page: https://www.devkult.com/regex/password