Aadhaar Validation for Developers

The correct regex, the Verhoeff checksum in ten languages, an HTML input snippet, and a checklist for handling Aadhaar numbers responsibly in forms and APIs.

The rules in one place

A string is a well-formed Aadhaar number when all of the following hold:

  1. After removing spaces (and dashes, if you accept them) it is exactly 12 ASCII digits.
  2. The first digit is 2–9. UIDAI never issues numbers starting with 0 or 1.
  3. The 12th digit is a valid Verhoeff check digit for the first 11.

Rules 1 and 2 are a regex. Rule 3 is not — a regex cannot compute a checksum, and about one in ten random 12-digit strings passes the checksum by chance, so skipping it lets most typos through. Full details of the algorithm are in the Verhoeff guide.

Aadhaar regex

Use this to check the shape before running the checksum:

Regex (no spaces)
^[2-9][0-9]{11}$

If you accept the printed format with spaces between groups:

Regex (optional spaces)
^[2-9][0-9]{3}\s?[0-9]{4}\s?[0-9]{4}$

Prefer stripping whitespace first and using the plain pattern — it is simpler to reason about and avoids accepting a stray space in the middle of a group.

HTML form input

Use a numeric keyboard on phones, cap the length, and let the browser do a first shape check. Never set autocomplete to a value that lets the browser store the number.

HTML
<label for="aadhaar">Aadhaar number</label>
<input id="aadhaar" name="aadhaar" type="text"
       inputmode="numeric" autocomplete="off"
       pattern="[2-9][0-9]{3}\s?[0-9]{4}\s?[0-9]{4}"
       maxlength="14" placeholder="XXXX XXXX XXXX"
       title="12-digit Aadhaar number">
<!-- pattern catches the shape; run the Verhoeff check in JS before submit -->

Verhoeff validation in 10 languages

Each snippet is self-contained and dependency-free. Select a language, copy, and add a unit test using the test numbers.

JavaScript
// Verhoeff tables (dihedral group D5)
const d = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
  [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
  [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
  [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
  [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
  [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
  [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
  [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
];
const p = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
  [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
  [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
  [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
  [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
  [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
  [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]
];

function verhoeffValid(digits) {
  let c = 0;
  const arr = String(digits).split('').reverse();
  for (let i = 0; i < arr.length; i++) {
    c = d[c][p[i % 8][Number(arr[i])]];
  }
  return c === 0;
}

/** True when s is a well-formed Aadhaar number (spaces allowed). */
function isValidAadhaar(input) {
  const s = String(input).replace(/\s/g, '');
  return /^[2-9]\d{11}$/.test(s) && verhoeffValid(s);
}

// isValidAadhaar('9999 4105 7058') === true  (UIDAI sandbox number)
// isValidAadhaar('999941057059')  === false (bad check digit)

Generating check digits and test data

Generation is the same loop with the position shifted by one, followed by the inverse table. Use it only for test fixtures — never to fabricate identity data.

JavaScript
const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];

// Check digit to append to an 11-digit body.
function verhoeffCheckDigit(body) {
  let c = 0;
  const arr = String(body).split('').reverse();
  for (let i = 0; i < arr.length; i++) {
    c = d[c][p[(i + 1) % 8][Number(arr[i])]];   // note the +1
  }
  return inv[c];
}

// Random, checksum-valid test number. First digit 2–9.
function testAadhaar() {
  let body = String(2 + Math.floor(Math.random() * 8));
  for (let i = 0; i < 10; i++) body += Math.floor(Math.random() * 10);
  return body + verhoeffCheckDigit(body);
}

Ready-made numbers, including the ones UIDAI publishes for its sandbox, are on the test Aadhaar numbers page.

Aadhaar Virtual ID (16 digits)

A VID is a 16-digit number that can be used in place of an Aadhaar number for authentication. It uses the same Verhoeff checksum, so the same verhoeffValid function works — only the length and regex change: ^[2-9][0-9]{15}$. If your form accepts both, branch on length. See the VID validator.

Handling Aadhaar numbers responsibly

Validation is the easy part. Under the Aadhaar Act and the Digital Personal Data Protection Act, 2023, the number itself is sensitive. A practical checklist:

  • Validate client-side first so typos are caught before the number is ever transmitted.
  • Mask on display: show only the last four digits (XXXX XXXX 7058), which is how UIDAI's own "masked Aadhaar" works.
  • Never log the full number. Scrub it from application logs, crash reports and analytics events.
  • Encrypt at rest and restrict who can decrypt. Store a hash or a reference token if you only need to match, not read.
  • Use test numbers in non-production. Real Aadhaar numbers do not belong in staging databases or fixtures.
  • Don't treat a checksum pass as verification. For KYC, use UIDAI-authorised authentication or offline e-KYC. See how to properly verify an Aadhaar number.

Excel and Google Sheets

There is no built-in spreadsheet function for Verhoeff and the formula version is unreadable. Paste your column into the bulk validator instead; it runs locally and exports a CSV you can paste back.