Validating UK Postcodes in JavaScript, Python and SQL
Postcodes · August 2026 · 6 min read
Copy-paste implementations of correct UK postcode validation, normalisation and formatting, with the test cases that catch the usual mistakes.
Correct UK postcode validation is about fifteen lines of code in any language. Here it is, plus the tests that prove it works.
The rules, restated
- Six permitted formats:
A9 9AA,A99 9AA,A9A 9AA,AA9 9AA,AA99 9AA,AA9A 9AA. - First position: not Q, V, X. Second position: not I, J, Z.
- Third position when a letter: A–H, J, K, P, S, T, U, W. Fourth position when a letter: A, B, E, H, M, N, P, R, V, W, X, Y.
- The final two letters exclude C, I, K, M, O, V.
GIR 0AAis valid and matches none of the formats.- The space always sits three characters from the end.
JavaScript / TypeScript
const UNIT = 'ABDEFGHJLNPQRSTUWXYZ';
const FIRST = 'ABCDEFGHIJKLMNOPRSTUWYZ';
const SECOND = 'ABCDEFGHKLMNOPQRSTUVWXY';
const THIRD = 'ABCDEFGHJKPSTUW';
const FOURTH = 'ABEHMNPRVWXY';
export const UK_POSTCODE = new RegExp(
'^(?:GIR ?0AA|(?:' +
`[${FIRST}](?:[0-9][0-9]?|[0-9][${THIRD}])|` +
`[${FIRST}][${SECOND}](?:[0-9][0-9]?|[0-9][${FOURTH}])` +
`) ?[0-9][${UNIT}]{2})
Validating UK Postcodes in JavaScript, Python and SQL | UK Random Address Generator
,
'i'
);
export const normalise = (value: string): string =>
value.toUpperCase().replace(/\s+/g, '');
export const format = (value: string): string => {
const compact = normalise(value);
return compact.length < 5 ? compact : `${compact.slice(0, -3)} ${compact.slice(-3)}`;
};
export const isValidPostcode = (value: string): boolean =>
UK_POSTCODE.test(format(value));
export const parts = (value: string) => {
if (!isValidPostcode(value)) return null;
const [outward, inward] = format(value).split(' ');
return {
outward,
inward,
area: outward.match(/^[A-Z]{1,2}/)![0],
district: outward,
sector: `${outward} ${inward[0]}`,
unit: inward.slice(1)
};
};
Python
import re
UNIT = "ABDEFGHJLNPQRSTUWXYZ"
FIRST = "ABCDEFGHIJKLMNOPRSTUWYZ"
SECOND = "ABCDEFGHKLMNOPQRSTUVWXY"
THIRD = "ABCDEFGHJKPSTUW"
FOURTH = "ABEHMNPRVWXY"
UK_POSTCODE = re.compile(
rf"^(?:GIR ?0AA|(?:"
rf"[{FIRST}](?:[0-9][0-9]?|[0-9][{THIRD}])|"
rf"[{FIRST}][{SECOND}](?:[0-9][0-9]?|[0-9][{FOURTH}])"
rf") ?[0-9][{UNIT}]{{2}})quot;,
re.IGNORECASE,
)
def normalise(value: str) -> str:
return "".join(value.upper().split())
def format_postcode(value: str) -> str:
compact = normalise(value)
return compact if len(compact) < 5 else f"{compact[:-3]} {compact[-3:]}"
def is_valid(value: str) -> bool:
return bool(UK_POSTCODE.match(format_postcode(value)))
PostgreSQL
Store the normalised form and validate with a check constraint:
ALTER TABLE addresses
ADD CONSTRAINT postcode_format CHECK (
upper(replace(postcode, ' ', '')) ~
'^(GIR0AA|([ABCDEFGHIJKLMNOPRSTUWYZ]([0-9][0-9]?|[0-9][ABCDEFGHJKPSTUW])|[ABCDEFGHIJKLMNOPRSTUWYZ][ABCDEFGHKLMNOPQRSTUVWXY]([0-9][0-9]?|[0-9][ABEHMNPRVWXY]))[0-9][ABDEFGHJLNPQRSTUWXYZ]{2})#39;
);
-- Index the outward code for district-level queries
CREATE INDEX addresses_outward_idx
ON addresses (split_part(postcode, ' ', 1));
The test suite
const valid = [
'SW1A 1AA', 'M1 1AE', 'B33 8TH', 'CR2 6XH', 'DN55 1PT',
'EC1A 1BB', 'W1A 0AX', 'GIR 0AA', 'BT1 1AA', 'G1 1XW',
'CF10 1EP', 'EH1 1BB', 'sw1a1aa', 'ec1a 1bb'
];
const invalid = [
'QW1A 1AA', // Q in the first position
'SI1 1AA', // I in the second position
'M1 1AC', // C in the unit
'SW1A 1AAA', // too long
'SW1A1', // too short
'12345', // not a postcode
'' // empty
];
What validation cannot tell you
A regular expression proves that a postcode is well formed. It cannot tell you that the postcode exists, that it belongs to the town the user typed, or that a specific property receives post there. Those questions need address data — Royal Mail's Postcode Address File under licence, or the ONS Postcode Directory for statistical geography.
For test data, structural validity is exactly what you want: use the postcode generator for valid examples, or check a specific one with the address and postcode validator.
More guides
- The UK Postcode Format Explained: Areas, Districts, Sectors and Units
- Royal Mail Address Formatting: How to Lay Out a UK Address Correctly
- How UK Address Generators Work — and How to Tell a Good One
- Using Real Customer Data in Test Environments: The UK GDPR Problem
- Why Your Address Form Rejects Perfectly Valid UK Addresses
- The UK Telephone Numbering Plan, and the Numbers Reserved for Fiction
- Where UK Address Data Comes From: PAF, ONSPD, OS Open Names and AddressBase