UK Postcode Regex
Validates a United Kingdom postcode across all standard outward-code formats (area + district) plus the fixed-format inward code, including the special GIR 0AA Girobank postcode.
Regex Pattern
^(GIR 0AA|((([A-Z][0-9]{1,2})|([A-Z][A-HJ-Y][0-9]{1,2})|([A-Z][0-9][A-Z])|([A-Z][A-HJ-Y][0-9][A-Z]?))\s?[0-9][A-Z]{2}))$Default flags: i
Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| ^ | Anchors the match to the start of the string |
| GIR 0AA | Special-case literal match for the historic Girobank postcode |
| [A-Z][0-9]{1,2} | Outward code format: one letter followed by one or two digits (e.g. M1, W1, N19) |
| [A-Z][A-HJ-Y][0-9]{1,2} | Outward code format: two letters (second restricted to valid area letters) followed by one or two digits (e.g. SW1, EC1) |
| [A-Z][0-9][A-Z] | Outward code format: one letter, one digit, one letter (e.g. W1A) |
| [A-Z][A-HJ-Y][0-9][A-Z]? | Outward code format: two letters, one digit, and an optional trailing letter (e.g. EC1A) |
| \s? | An optional single space between the outward code and the inward code |
| [0-9][A-Z]{2} | Inward code: exactly one digit followed by exactly two letters (e.g. 1AA) |
| $ | Anchors the match to the end of the string |
Detailed Explanation
What it does
This pattern validates that a string is a syntactically correct UK postcode, covering all recognized outward-code shapes (area letter(s) plus district number, with an optional trailing letter) combined with the fixed three-character inward code (digit + two letters), plus the special-cased 'GIR 0AA'.
Why it works
UK postcodes follow a small, well-documented set of outward-code shapes defined by Royal Mail: one letter + 1-2 digits, two letters + 1-2 digits, one letter + digit + letter, or two letters + digit + optional letter. The second letter in two-letter areas excludes I and Z (represented by [A-HJ-Y]) because those letters are not used in that position. All four shapes are joined in an alternation, followed by an optional space and the mandatory inward code, which is always exactly one digit and two letters. The i flag makes the whole match case-insensitive so lowercase postcodes are also accepted, and the GIR 0AA branch handles the one well-known postcode that doesn't fit the general pattern.
Common use cases
- Validating postcode fields in UK shipping and checkout forms
- Cleaning and standardizing address data imported from spreadsheets or third-party APIs
- Client-side form validation before calling a postcode lookup or geocoding service
- Filtering log or support-ticket text for strings that look like UK postcodes
Edge cases
- Postcodes with and without the internal space, like 'SW1A 1AA' and 'SW1A1AA', are both accepted since the space is optional
- Lowercase input, such as 'sw1a 1aa', is accepted because of the case-insensitive i flag
- The historic 'GIR 0AA' Girobank postcode is explicitly matched even though it doesn't follow the standard area/district shape
- Postcodes using the letter I or Z as the second area letter (not used by Royal Mail in that position) are correctly rejected by the [A-HJ-Y] restriction
Limitations
- Validates format only, not whether the postcode actually exists; use a postcode lookup API (e.g. Royal Mail PAF or a third-party service) to confirm real-world validity
- Does not normalize whitespace or casing; apply .toUpperCase() and normalize spacing in application code before storing or displaying
- The exact set of valid single-letter and double-letter area codes changes rarely but is not fully hard-coded here; some genuinely unused area codes may still pass this format check
- Does not handle British Forces Post Office (BFPO) codes, which use a different numbering scheme entirely
Interactive Tester
Edit the pattern or text below — matching runs live in your browser.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const ukPostcodeRegex = /^(GIR 0AA|((([A-Z][0-9]{1,2})|([A-Z][A-HJ-Y][0-9]{1,2})|([A-Z][0-9][A-Z])|([A-Z][A-HJ-Y][0-9][A-Z]?))\s?[0-9][A-Z]{2}))$/i;
console.log(ukPostcodeRegex.test('SW1A 1AA')); // trueCommon Mistakes
Using a simplistic pattern like ^[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}$ that doesn't account for the restricted second-area-letter set, letting invalid letters like I or Z slip in
Fix: Restrict the second area letter with [A-HJ-Y] to match Royal Mail's actual allowed letters in that position
Forgetting the special-cased 'GIR 0AA' Girobank postcode, which doesn't fit any of the standard outward-code shapes
Fix: Add an explicit alternative for 'GIR 0AA' at the start of the pattern
Requiring the internal space between outward and inward codes, rejecting valid unspaced input like 'SW1A1AA' often produced by form autofill or database exports
Fix: Make the space optional with \s? rather than a mandatory literal space
Performance Notes
- The outward-code alternation has four small, mutually distinguishable branches, so backtracking cost is minimal even for non-matching input
- Anchoring with ^ and $ lets the engine reject clearly malformed postcodes (wrong length, invalid characters) very quickly
- For bulk address validation, normalize casing and whitespace once during import rather than relying on the i flag and \s? on every subsequent comparison
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |