Validar DNI (NIF), CIF, NIE
¿Buscas un comprobador de DNI para tu formulario? Aquí tienes cómo validar DNI (NIF), CIF, NIE en PHP y en JavaScript con una sola función. Validar DNI, NIF, CIF y NIE consiste en comprobar el formato y la letra o cifra de control; no se consulta ninguna base de datos.
Comprobador de DNI, NIF, CIF y NIE en línea
Escribe un número y pulsa el botón. Se comprueba en tu navegador con la misma lógica de la función de más abajo; no se envía a ningún servidor.
Qué son el DNI, el NIF, el NIE y el CIF
Los cuatro son números de identificación, pero no son lo mismo:
- DNI: el número del Documento Nacional de Identidad, de 8 cifras. Con su letra de verificación forma el NIF de las personas físicas de nacionalidad española.
- NIF: número de identificación fiscal. Para un español con DNI es el DNI con su letra. Para las empresas y entidades, desde 2008 (Real Decreto 1065/2007 y Orden EHA/451/2008) el antiguo CIF es su NIF.
- NIE: número de identidad de extranjero. Es el NIF de las personas físicas sin nacionalidad española: una letra (
X,YoZ), 7 cifras y una letra de control. - CIF: código de identificación fiscal de las sociedades y entidades. Ya no es el nombre oficial (ahora es el NIF de la entidad), pero se sigue usando y su estructura no ha cambiado: una letra, 7 cifras y un carácter de control.
| Tipo | Estructura | Ejemplo |
|---|---|---|
| DNI / NIF de persona física | 8 cifras + letra | 12345678Z |
| NIE | X, Y o Z + 7 cifras + letra | X1234567L |
NIF especial (K, L, M) | K, L o M + 7 cifras + letra | K1234567L |
| CIF (NIF de entidades) | letra de clave + 7 cifras + cifra o letra de control | B12345674 |
Los NIF especiales los asigna la Agencia Tributaria a quien no tiene DNI ni NIE: la K a los españoles menores de 14 años que residen en España, la L a los españoles residentes en el extranjero y la M a los extranjeros sin NIE. Valen mientras su titular no obtenga el DNI o el NIE. El Reglamento general de gestión e inspección tributaria los define con una letra inicial, siete caracteres alfanuméricos y un carácter de verificación alfabético (artículos 19 a 21 del Real Decreto 1065/2007). Aunque la norma dice «alfanuméricos», la letra de control se calcula sobre un número, así que la función solo acepta cifras en esas siete posiciones.
La letra inicial del CIF indica la forma jurídica: A sociedades anónimas, B sociedades de responsabilidad limitada, C sociedades colectivas, D sociedades comanditarias, E comunidades de bienes, herencias yacentes y demás entidades sin personalidad jurídica no incluidas en otras claves, F cooperativas, G asociaciones, H comunidades de propietarios, J sociedades civiles, P corporaciones locales, Q organismos públicos, R congregaciones e instituciones religiosas, S órganos de la Administración del Estado y de las comunidades autónomas, U uniones temporales de empresas y V otros tipos. La N es para entidades extranjeras y la W para establecimientos permanentes de entidades no residentes. Son las claves de la Orden EHA/451/2008, con el artículo 3 en la redacción de la Orden HAP/5/2016.
Cómo validar DNI, NIF, CIF y NIE: el algoritmo
Letra del DNI (NIF de persona física)
El Ministerio del Interior describe el cálculo del dígito de control del NIF y el NIE: se divide el número entre 23 y el resto se sustituye por una letra de 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 |
Es la cadena TRWAGMYFPDXBNJZSQVHLCKE, donde la posición de cada letra es su resto. Con 12345678, el resto de dividir entre 23 es 14 y la letra es la Z: 12345678Z.
Letra del NIE
Se sustituye la X por 0, la Y por 1 y la Z por 2, y se aplica el mismo algoritmo que al DNI. Para X1234567L: X1234567 pasa a 01234567, que dividido entre 23 da resto 19, y la letra 19 de la tabla es la L.
Letra de los NIF especiales K, L y M
La letra de control se calcula como la de un DNI sobre las siete cifras, sin la letra inicial: para K1234567L, 1234567 entre 23 da resto 19, que es la L. El Reglamento solo exige que el carácter de verificación sea una letra; calcularla como un DNI es lo que hacen también otras librerías de validación, pero no he encontrado el algoritmo escrito en una norma.
Control del CIF
El carácter de control se calcula con las 7 cifras centrales:
- Sumar las cifras de las posiciones pares (la 2.ª, la 4.ª y la 6.ª). Es la suma A.
- Multiplicar por 2 cada cifra de las posiciones impares (la 1.ª, la 3.ª, la 5.ª y la 7.ª) y sumar las cifras del resultado:
8 × 2 = 16aporta1 + 6 = 7. Es la suma B. - Sumar A + B y quedarse con la cifra de las unidades.
- Si esa cifra es 0, el control es 0. Si no, el control es 10 menos esa cifra.
- El control puede escribirse como esa cifra o como una letra:
J= 0,A= 1,B= 2,C= 3,D= 4,E= 5,F= 6,G= 7,H= 8,I= 9.
Con B12345674, las cifras son 1234567. Pares: 2 + 4 + 6 = 12. Impares: 1 × 2 = 2, 3 × 2 = 6, 5 × 2 = 10 (aporta 1) y 7 × 2 = 14 (aporta 5): suma 14. Total 26, unidades 6, control 10 − 6 = 4. El control es el 4 o su letra, la D: B12345674 y B1234567D cuadran con el algoritmo.
¿Cifra o letra? Es habitual leer que las entidades con clave A, B, E y H llevan siempre cifra y las P, Q, R, S, N y W siempre letra (lo recoge, por ejemplo, la Wikipedia, con más matices). Pero el BOE solo habla de «carácter de control» y no lo detalla, así que la función acepta las dos formas en todas las claves: así nunca rechaza un CIF real cuyo control cuadre. Si prefieres aplicar esa regla, en PHP añade estas dos líneas justo antes del último return:
if (strpos('PQRSNW', $cif[0]) !== false) return $cif[8] === substr('JABCDEFGHI', $n, 1); // solo letra
if (strpos('ABEH', $cif[0]) !== false) return $cif[8] === (string) $n; // solo cifraValidar DNI (NIF), CIF, NIE en PHP
La función validDniCifNie() recibe el número, con o sin espacios, guiones o puntos y en mayúsculas o minúsculas, y devuelve true o false:
/**
* Validar DNI (NIF), CIF, NIE
*
* @param string $dni Número de identificación (con o sin espacios, guiones o puntos; en mayúsculas o minúsculas)
*
* @return bool true si es válido; false si no
*/
function validDniCifNie($dni){
if (!is_scalar($dni)){
return false; // un array o un objeto (p. ej. dni[]= en un formulario manipulado) no es un DNI
}
// Se ignoran espacios (también los de Unicode, como el espacio duro al pegar de un PDF), guiones y puntos
$cif = strtoupper((string) preg_replace('/[\t\n\x0B\f\r\p{Zs}\x{2028}\x{2029}\x{FEFF}\x{2010}-\x{2015}\x{2212}.-]+/u', '', (string) $dni));
$letras = 'TRWAGMYFPDXBNJZSQVHLCKE';
// DNI/NIF de persona física: 8 cifras y una letra
if (preg_match('/^[0-9]{8}[A-Z]$/', $cif)){
return $cif[8] === $letras[(int) substr($cif, 0, 8) % 23];
}
// NIE: X, Y o Z + 7 cifras + letra. X, Y, Z valen 0, 1, 2 y se calcula como un DNI
if (preg_match('/^[XYZ][0-9]{7}[A-Z]$/', $cif)){
$numero = strtr($cif[0], 'XYZ', '012') . substr($cif, 1, 7);
return $cif[8] === $letras[(int) $numero % 23];
}
// NIF especiales K, L y M: 7 cifras (sin la letra inicial) y letra de control calculada como un DNI
if (preg_match('/^[KLM][0-9]{7}[A-Z]$/', $cif)){
return $cif[8] === $letras[(int) substr($cif, 1, 7) % 23];
}
// CIF (NIF de entidades): letra de la clave + 7 cifras + control (cifra o letra de la A a la J)
if (!preg_match('/^[ABCDEFGHJNPQRSUVW][0-9]{7}[0-9A-J]$/', $cif)){
return false;
}
$suma = 0;
for ($i = 1; $i <= 7; $i++){
$d = (int) $cif[$i];
if ($i % 2 == 1){ // posiciones impares: se doblan y se suman sus cifras (16 -> 1 + 6 = 7)
$d *= 2;
$d = $d > 9 ? $d - 9 : $d;
}
$suma += $d;
}
$n = (10 - $suma % 10) % 10;
// El control puede ser la cifra o su letra (J = 0, A = 1 ... I = 9)
return $cif[8] === (string) $n || $cif[8] === substr('JABCDEFGHI', $n, 1);
}Uso:
var_dump(validDniCifNie('12345678Z')); // bool(true) DNI
var_dump(validDniCifNie('12.345.678-z')); // bool(true) DNI con puntos, guion y minúscula
var_dump(validDniCifNie('x1234567l')); // bool(true) NIE
var_dump(validDniCifNie('B12345674')); // bool(true) CIF
var_dump(validDniCifNie('12345678A')); // bool(false) letra equivocadaValidar DNI, NIF, CIF y NIE en JavaScript
La misma lógica para comprobarlo en el navegador antes de enviar el formulario (es la que usa el comprobador de arriba). Con los mismos datos, la versión PHP y la de JavaScript dan el mismo resultado, también con espacios y guiones raros pegados de un PDF:
/**
* Validar DNI (NIF), CIF, NIE
* @param {string} dni Número de identificación (con o sin espacios, guiones o puntos; en mayúsculas o minúsculas)
* @returns {boolean} true si es válido; false si no
*/
function validDniCifNie(dni) {
if (typeof dni !== 'string' && typeof dni !== 'number') return false;
// Se ignoran espacios, guiones y puntos; solo se pasan a mayúsculas las letras a-z
const cif = String(dni).replace(/[\s\u2010-\u2015\u2212.-]+/g, '').replace(/[a-z]/g, (c) => c.toUpperCase());
const letras = 'TRWAGMYFPDXBNJZSQVHLCKE';
// DNI/NIF de persona física: 8 cifras y una letra
if (/^[0-9]{8}[A-Z]$/.test(cif)) {
return cif[8] === letras[parseInt(cif.slice(0, 8), 10) % 23];
}
// NIE: X, Y o Z + 7 cifras + letra. X, Y, Z valen 0, 1, 2 y se calcula como un DNI
if (/^[XYZ][0-9]{7}[A-Z]$/.test(cif)) {
const numero = 'XYZ'.indexOf(cif[0]) + cif.slice(1, 8);
return cif[8] === letras[parseInt(numero, 10) % 23];
}
// NIF especiales K, L y M: 7 cifras (sin la letra inicial) y letra de control calculada como un DNI
if (/^[KLM][0-9]{7}[A-Z]$/.test(cif)) {
return cif[8] === letras[parseInt(cif.slice(1, 8), 10) % 23];
}
// CIF (NIF de entidades): letra de la clave + 7 cifras + control (cifra o letra de la A a la J)
if (!/^[ABCDEFGHJNPQRSUVW][0-9]{7}[0-9A-J]$/.test(cif)) {
return false;
}
let suma = 0;
for (let i = 1; i <= 7; i++) {
let d = Number(cif[i]);
if (i % 2 === 1) { // posiciones impares: se doblan y se suman sus cifras (16 -> 1 + 6 = 7)
d *= 2;
d = d > 9 ? d - 9 : d;
}
suma += d;
}
const n = (10 - suma % 10) % 10;
// El control puede ser la cifra o su letra (J = 0, A = 1 ... I = 9)
return cif[8] === String(n) || cif[8] === 'JABCDEFGHI'[n];
}Casos probados
Estos casos se han ejecutado con la función PHP (en PHP 7.2, 7.4, 8.2, 8.3, 8.4 y 8.5) y con la de JavaScript (en Node 24). Los números válidos son de ejemplo: cumplen el algoritmo, pero no pertenecen, que se sepa, a nadie.
| Entrada | ¿Válido? | Qué comprueba |
|---|---|---|
12345678Z | Sí | DNI válido (el ejemplo del Ministerio del Interior) |
12345678A | No | DNI con la letra cambiada |
12345678z | Sí | DNI en minúsculas |
12345678-Z | Sí | DNI con guion |
12.345.678-Z | Sí | DNI con puntos y guion |
1234567Z | No | DNI con 7 cifras |
12345678 | No | DNI sin letra |
| (vacío) | No | Cadena vacía |
X1234567L | Sí | NIE con X |
Y1234567X | Sí | NIE con Y |
Z1234567R | Sí | NIE con Z |
X1234567A | No | NIE con la letra cambiada |
K1234567L | Sí | NIF especial K: 7 cifras, 1234567 entre 23 da resto 19 (L) |
L1234567L | Sí | NIF especial L |
M1234567L | Sí | NIF especial M |
K1234567A | No | NIF especial K con la letra cambiada |
K1234567D | No | NIF especial K con letra de control tipo CIF (la versión anterior lo aceptaba) |
T12345671 | No | Empieza por T y acaba en 1 (la versión anterior lo trataba como NIE y lo daba por válido) |
A12345674 | Sí | CIF tipo A con cifra de control |
A1234567D | Sí | CIF tipo A con letra de control: se acepta (ver «Control del CIF») |
A12345675 | No | CIF con la cifra de control cambiada |
B12345674 | Sí | CIF tipo B (siempre cifra) |
P1234567D | Sí | CIF tipo P con letra de control |
P12345674 | Sí | CIF tipo P con cifra de control: se acepta |
G12345674 | Sí | CIF tipo G con cifra (admite las dos) |
G1234567D | Sí | CIF tipo G con letra |
I12345674 | No | Letra inicial I: no es una clave de entidad |
A1234567E | No | CIF con la letra de control cambiada |
Cosas que conviene saber
- Valida el formato y el control, no que exista. Un número puede cuadrar con el algoritmo y no estar asignado a nadie. Para comprobar que existe hay que consultar a la Agencia Tributaria.
- Un DNI con menos de 8 cifras no se acepta tal cual. Si en tu base de datos se guardó como número y perdió los ceros de la izquierda, rellénalo con ceros antes de validarlo.
- Lo que empieza por
Tno se valida. La versión anterior de esta función lo trataba como un NIE y comparaba el último carácter con el resultado depreg_match(), que es 1 o 0, así que daba por bueno un número que empezaba porTsolo si acababa en1. No he encontrado ninguna norma con esos números, así que la función los rechaza. - Los NIF
K,LyMse calculan sobre 7 cifras. La versión anterior tomaba también la letra de control como parte del número: el resultado salía bien, pero PHP avisaba («A non well formed numeric value» en PHP 7 y «A non-numeric value encountered» en PHP 8). Además aceptaba en ellos una letra de control tipo CIF, como enK1234567D; ahora no. - Si no es texto, devuelve
false. Un array (por ejemplo, undni[]=en un formulario manipulado) o un objeto ya no provocan avisos ni errores. - Se ignoran espacios, guiones y puntos, también el espacio duro y los guiones largos que aparecen al pegar desde un PDF:
12.345.678-Zy12 345 678 Zson válidos. - La función PHP funciona en PHP 7 y 8 (probada de la 7.2 a la 8.5) y no necesita ninguna extensión.
Si en el mismo formulario pides una cuenta bancaria, aquí tienes cómo validar IBAN español en PHP y JavaScript.
4 comentarios
Hola Juan,
Si he probado el script cuando me hacia falta y no me dio errores, acabo de probar con el que has pasado y da conforme es valido.
Revisando en otras webs de validadores indica lo mismo que dicho NIE es valido.
Cuando lo publique no mencione de donde es el código porque no recuerdo ya que hace mucho tiempo que lo tengo como puedes comprobar dicha publicación es del 2020 y hace mas tiempo que lo uso.Si puedes pásame la corrección y reviso.
Muchas gracias por leer la pagina web, que tengas un buen día.
Saludos
No funciona con CIFs de empresas que no residen en España. La mayoría de los algoritmos no tienen en cuenta. Es una pena. Las únicas webs que funcionan, no tiene código, solo comprobación online.
Hola, para eso necesitarías tener una función por PAIS (ya que cada uno tiene sus validaciones), para poder validar teniendo en cuenta las 2 letras iniciales ej.: ESB12345678 o ADF-099999-G (Andorra), también existen paginas donde te dan una API y puedes hacer validaciones lo unico que son de pago.
Lo has probado el script? Te invito a que lo compruebes con un NIE, por ejemplo:
Y8506795Y
Ya que lo has copiado de otra web, sería lo suyo que al menos comprobaras su funcionamiento completo.
Si lo quieres, te paso la correción.