Letra del DNI y del NIE: algoritmo oficial y código
Cómo se calcula la letra del DNI y del NIE (módulo 23), con código en JavaScript, PHP y Python, casos límite y por qué una letra correcta no prueba nada.
Por Equipo Constaia5 min de lectura
La letra del DNI es la validación más barata que existe: una división y una tabla de 23 letras. Aun así, en formularios reales falla más de lo que parece, por culpa de espacios, guiones, minúsculas, ceros a la izquierda y NIE que empiezan por letra.
Aquí tienes el algoritmo oficial, código listo para copiar en tres lenguajes y los casos límite que conviene cubrir. Y al final, una advertencia importante: una letra correcta no demuestra que el documento exista.
El algoritmo oficial
El Ministerio del Interior lo describe así: se divide el número entre 23 y el resto se sustituye por una letra según esta tabla.
| Resto | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Letra | T | R | W | A | G | M | Y | F | P | D | X | B |
| Resto | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Letra | N | J | Z | S | Q | V | H | L | C | K | E |
Escrita seguida, la tabla es la cadena TRWAGMYFPDXBNJZSQVHLCKE: la letra es el carácter en la posición número % 23.
El ejemplo oficial: 12345678 entre 23 da resto 14, así que la letra es Z → 12345678Z.
El NIE
El NIE de las personas extranjeras tiene una letra inicial (X, Y o Z), siete cifras y la letra de control. Para calcularla se sustituye la letra inicial por un número y se aplica el mismo algoritmo (fuente):
| Letra inicial | Se sustituye por |
|---|---|
| X | 0 |
| Y | 1 |
| Z | 2 |
Ejemplos:
X1234567→01234567→ resto 19 → L →X1234567LY1234567→11234567→ resto 10 → X →Y1234567XZ1234567→21234567→ resto 1 → R →Z1234567R
Haz la cuenta con el número completo tras la sustitución (8 cifras). Si solo divides las 7 cifras del NIE, X coincide por casualidad (el 0 inicial no cambia nada), pero Y y Z darán letras incorrectas.
El código
Las tres versiones hacen lo mismo: normalizan la entrada, comprueban el formato (8 cifras + letra, o X/Y/Z + 7 cifras + letra) y comparan la letra.
const LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE";
export function validateNifNie(input) {
const value = String(input).toUpperCase().replace(/[\s.\-]/g, "");
const match = /^([XYZ]\d{7}|\d{8})([A-Z])$/.exec(value);
if (!match) return { valid: false, reason: "format" };
const [, body, letter] = match;
const number = Number(body.replace(/^[XYZ]/, (c) => "XYZ".indexOf(c)));
const expected = LETTERS[number % 23];
return { valid: letter === expected, expected };
}
validateNifNie("12345678Z"); // { valid: true, expected: "Z" }
validateNifNie("x-1234567-l"); // { valid: true, expected: "L" }
validateNifNie("12345678A"); // { valid: false, expected: "Z" }Hemos probado los tres fragmentos con los ejemplos de este artículo.
Casos límite que sí aparecen en formularios
Minúsculas, espacios, puntos y guiones
La gente escribe 12.345.678-z, 12345678 z o x1234567l. Normaliza antes de validar: mayúsculas y fuera separadores. Guarda después la forma normalizada (12345678Z), no lo que escribió el usuario.
Ceros a la izquierda
Un DNI puede empezar por cero. El problema aparece cuando el dato pasa por una hoja de cálculo o por una columna numérica: 01234567L se convierte en 1234567L y el formato de 8 cifras falla. La letra no cambia (los ceros a la izquierda no alteran el resto), así que puedes rellenar con ceros hasta 8 cifras antes de validar:
const padded = value.replace(/^(\d{1,7})([A-Z])$/, (_, n, l) => n.padStart(8, "0") + l);Hazlo solo si sabes que el origen pierde ceros; si no, es mejor rechazar y pedir el número completo.
Letra ausente
Muchos formularios antiguos guardan solo el número. Puedes calcular la letra para mostrarla, pero no la "completes" en silencio: si el usuario se equivocó en una cifra, le estarás fabricando un NIF válido que no es el suyo.
Letras que no están en la tabla
La tabla no incluye I, Ñ, O ni U. Si llega una de ellas en la posición de control, el formato es correcto pero la letra nunca coincidirá: la validación devuelve false, que es lo esperado.
Otros NIF: CIF y NIF especiales
- Lo que antes se llamaba CIF, hoy NIF de personas jurídicas y entidades, tiene otra estructura: según la Orden EHA/451/2008, una letra que indica la forma jurídica (A sociedades anónimas, B limitadas, G asociaciones…), siete cifras y un carácter de control. Su control no se calcula con la tabla del DNI.
- Las personas extranjeras sin NIE que necesitan un NIF reciben uno que empieza por M (RD 1065/2007, art. 20).
El código de este artículo cubre DNI y NIE. Si necesitas validar también CIF y NIF especiales, prueba nuestra herramienta gratuita, que los detecta automáticamente.
Lo que la letra no te dice
La letra de control existe para detectar errores de tecleo: una cifra cambiada o dos cifras intercambiadas. No es un mecanismo de seguridad:
- Cualquiera puede calcular la letra de cualquier número. Hay 23 letras posibles; con el algoritmo público, generar un NIF "válido" es trivial.
- Una letra correcta no dice que el número exista, que pertenezca a la persona del formulario ni que el documento esté en vigor.
Por eso la letra es un primer filtro (útil, barato, inmediato en el propio formulario), no una verificación de identidad.
Cómo lo hace Constaia
En Constaia la letra es una de varias comprobaciones deterministas. Cuando analizas un DNI o un NIE, la respuesta incluye el check nif_check_digit junto con otros como mrz_checksums (dígitos de control de la MRZ) y mrz_matches_visual (que la MRZ coincide con lo impreso). Con checks.holder.document_number compruebas además que el número leído es el que el usuario escribió en el formulario.
curl https://api.constaia.com/v1/analyze \
-H "Authorization: Bearer ck_test_..." \
-F file=@nie.jpg \
-F 'options={"expect":["es_dni","es_nie"],"checks":{"not_expired":true,"holder":{"document_number":"X1234567L"}}}'Con una clave ck_test_… y el fichero nie.jpg obtienes una respuesta simulada sin consumir créditos (modo test). Aun así, recuerda: ni la letra ni la MRZ prueban que un documento sea auténtico, y Constaia no es un sistema KYC biométrico.
Los validadores de NIF/NIE, MRZ e IBAN forman el paquete open source @constaia/validators. Más detalles en Checks.
Resumen
- Letra =
TRWAGMYFPDXBNJZSQVHLCKE[número % 23]. - En el NIE, sustituye X/Y/Z por 0/1/2 antes de dividir.
- Normaliza (mayúsculas, sin separadores) y vigila los ceros a la izquierda.
- Una letra correcta solo descarta errores de tecleo.
Para comprobar un número suelto, usa el validador gratuito de NIF, NIE y CIF. Para validar documentos completos, crea una cuenta gratis con 250 créditos al mes.