Rexact
Cheatsheet

Regex French intra-EU VAT number

A French VAT number isn't just "FR" followed by 11 digits: the 2-character key after "FR" can also contain a letter (but never I or O, to avoid confusion with 1 and 0). Stack Overflow's top-voted answer, taken from the Regular Expressions Cookbook, makes no such exclusion and even makes the country prefix optional - letting a bare SIREN slip through. Here's a pattern that fixes both points, compared against that answer and two far more permissive attempts.

/^FR[0-9A-HJ-NP-Z]{2}[0-9]{9}$/i
passes 24/24 cases

Comparison of existing patterns

Implementation

const re = new RegExp("^FR[0-9A-HJ-NP-Z]{2}[0-9]{9}$", "i");
re.test(input);

Limits

  • Ne vérifie que la FORME du numéro (préfixe FR, clé de 2 caractères sans I ni O, 9 chiffres) : la clé réelle se calcule à partir du SIREN par [ 12 + 3 × (SIREN mod 97) ] mod 97, un calcul arithmétique qu'une regex ne peut pas effectuer — un numéro syntaxiquement valide peut donc correspondre à une clé incorrecte pour le SIREN donné.
  • Ne consulte pas le système VIES de la Commission européenne : seule une requête VIES confirme qu'un numéro est réellement attribué et actif, pas seulement bien formé.
  • Exige une chaîne sans séparateurs : les espaces ou tirets couramment utilisés dans l'usage courant (« FR 83 404 833 048 », « FR-83-404833048 ») doivent être retirés avant validation.
  • Ne détecte pas spécifiquement les deux confusions les plus fréquentes côté utilisateur — saisir le SIREN seul (9 chiffres, sans FR ni clé) ou le SIRET (SIREN + 5 chiffres d'établissement) à la place du numéro de TVA : ces deux cas sont simplement rejetés comme n'importe quelle chaîne mal formée, sans message dédié.

Related pages