PatternRegex

Match a credit card number

Confirm a card number field holds 13 to 19 digits, with optional spaces or dashes between groups, before you even think about Luhn.

Last updated

Pattern
^\d(?:[ -]?\d){12,18}$
Open in Regex Tester & Builder

Matches

  • 4111 1111 1111 1111
  • 4111-1111-1111-1111
  • 4111111111111111

Doesn’t match

  • 1234
  • 4111 1111 1111
  • 4111-1111-1111-abcd

Why it’s written this way

The pattern opens with a single \d, then repeats (?:[ -]?\d) between 12 and 18 more times — so the total digit count lands anywhere from 13 to 19, matching the range real card networks issue (16 for Visa and Mastercard, 15 for Amex, up to 19 for some debit and UnionPay cards).

Each digit after the first can be preceded by an optional space or dash, so '4111 1111 1111 1111' and '4111-1111-1111-1111' both pass alongside the unspaced form — the separator is permitted, never required.

Edge cases to know

  • Matching the shape is not validating the number — a 16-digit string that happens to fit the length range still needs a Luhn checksum pass before you treat it as a genuine card number.
  • It doesn't identify the card network or check issuer-specific prefixes; telling Visa from Mastercard needs a separate lookup table.
  • Card numbers are sensitive under PCI DSS — validating the format client-side is fine, but think carefully before this value ever reaches your own logs, database, or error reports unmasked.

Related in Patterns