IBAN validation trips people up because it looks like it should be a length check plus a regex, and it's neither. Here's the actual structure, in the order you should check it.
1. Length and country format. Every IBAN starts with a 2-letter ISO country code and 2 check digits, followed by a country-specific BBAN whose length is fixed per country — Germany is always 22 characters, the Netherlands 18, Malta 31. A string can be the right shape (letters-then-digits) and still be the wrong length for the country it claims to be from.
2. The mod-97 checksum (ISO 7064). This is the part people get wrong or skip:
function isValidIBAN(iban) {
const clean = iban.replace(/\s+/g, "").toUpperCase();
if (!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(clean)) return false;
const rearranged = clean.slice(4) + clean.slice(0, 4);
const numeric = rearranged.replace(/[A-Z]/g, c => (c.charCodeAt(0) - 55).toString());
// IBANs convert to numbers far too large for JS's Number type (30+ digits),
// so mod 97 has to be computed in chunks rather than in one BigInt/Number op.
let remainder = numeric;
while (remainder.length > 2) {
const chunk = remainder.slice(0, 9);
remainder = (parseInt(chunk, 10) % 97) + remainder.slice(chunk.length);
}
return parseInt(remainder, 10) % 97 === 1;
}
Move the first 4 characters to the end, convert every letter to its numeric value (A=10 ... Z=35), and the resulting number mod 97 must equal 1. The chunked-modulo loop is the detail that trips up most from-scratch implementations — you can't just do BigInt(numeric) % 97n in older runtimes without a BigInt polyfill, and plain Number silently loses precision past 2^53.
3. What this does not tell you. A passing checksum means the IBAN is well-formed — not that the account exists, is open, or belongs to who the payer thinks it does. Confirming that needs a live lookup against the bank (or a service like the account-name-matching checks banks now run for fraud prevention), which is a separate, heavier operation than format validation.
For most apps — checkout forms, payout setup, onboarding — steps 1 and 2 are exactly the right amount of validation: fast, no external calls, no third-party uptime dependency. I wrapped both (plus the per-country BBAN structure check) into an endpoint on Validate if you'd rather not own the mod-97 edge cases yourself.
Top comments (0)