PatternRegex

Validate a UK postcode

Validate the common shape of a UK postcode — outward code, optional space, inward code — case-insensitively.

Last updated

Patterni
^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$
Open in Regex Tester & Builder

Matches

  • SW1A 1AA
  • M1 1AE
  • EC1A 1BB

Doesn’t match

  • 12345
  • SW1A 1AA
  • not a postcode

Why it’s written this way

UK postcodes split into an outward code and an inward code. [A-Z]{1,2}\d[A-Z\d]? covers the outward part — one or two letters, a digit, then an optional letter or digit — which spans formats from 'M1' to 'SW1A'. The inward part, \d[A-Z]{2}, is always a digit followed by exactly two letters.

\s? makes the space between the two halves optional, since forms and copy-pasted addresses are inconsistent about including it, and the 'i' flag means the pattern doesn't care whether letters arrive as 'sw1a' or 'SW1A'.

Edge cases to know

  • →A handful of real postcodes (parts of London's 'W1' series, plus some Girobank and overseas-territory codes) don't fit this general shape — the full Royal Mail specification needs a longer, more irregular pattern.
  • →It accepts some letter-and-digit combinations that aren't currently allocated to any real postcode district — matching the shape isn't the same as the postcode existing.
  • →Two consecutive spaces, or none where one belongs, both need normalizing first — \s? only tolerates zero or one space, not several.

Part by part

^[A-Z]{1,2}
The area: one or two letters identifying a postal town, such as M for Manchester or SW for south-west London.
\d[A-Z\d]?
The district, which is a digit optionally followed by another digit or a letter. That optional character is what makes both W1 and W1A possible.
\s?
An optional space before the second half. Real postcodes are written with it and pasted data often is not, so accepting both is practical.
\d[A-Z]{2}$
The inward code: always a digit followed by two letters, with no exceptions. This half is the strictly regular part of the format.

Common mistakes

  • ^[A-Z]{1,2}\d{1,2}\s?\d[A-Z]{2}$

    Requiring digits in the district rejects the London codes with a trailing letter, such as W1A and EC1V — which covers a large number of central London addresses.

    ^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$
  • ^[A-Z]{1,2}\d[A-Z\d]?\s\d[A-Z]{2}$

    Requiring the space rejects the unspaced form that arrives from most databases and CSV exports, so valid data fails on import rather than at entry.

  • ^[A-Z0-9\s]{5,8}$

    Matching by length and alphabet abandons the structure entirely, so any five-to-eight-character alphanumeric string passes — which on an address form is barely a check at all.

Questions

Why is the second half so much stricter than the first?

Because it is genuinely uniform: the inward code is always a digit followed by two letters, everywhere in the country. The outward code varies because it grew historically, town by town, rather than being designed as a system.

Does this cover every valid postcode?

Not quite. A handful of special cases exist, including the non-geographic GIR 0AA and codes for British Overseas Territories, which do not follow the standard shape. Add them explicitly if your data might contain them.

Should I store the space?

Store it normalised — one space before the final three characters — because that is the form Royal Mail publishes and the form users recognise. Accept anything on input and normalise before saving.

Does a match mean the postcode exists?

No. Only a lookup against the Post Office address file can confirm that, and it also gives you the address, which is usually why you wanted the postcode in the first place.

Related in Patterns