<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nancy Powel</title>
    <description>The latest articles on DEV Community by Nancy Powel (@nancy_powel_6468dadf6f920).</description>
    <link>https://dev.to/nancy_powel_6468dadf6f920</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4181490%2F67061799-f990-4049-9b7a-330230206fe8.png</url>
      <title>DEV Community: Nancy Powel</title>
      <link>https://dev.to/nancy_powel_6468dadf6f920</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nancy_powel_6468dadf6f920"/>
    <language>en</language>
    <item>
      <title>Cómo validar una CURP en código: anatomía de los 18 caracteres y el dígito verificador</title>
      <dc:creator>Nancy Powel</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:42:26 +0000</pubDate>
      <link>https://dev.to/nancy_powel_6468dadf6f920/como-validar-una-curp-en-codigo-anatomia-de-los-18-caracteres-y-el-digito-verificador-4m2n</link>
      <guid>https://dev.to/nancy_powel_6468dadf6f920/como-validar-una-curp-en-codigo-anatomia-de-los-18-caracteres-y-el-digito-verificador-4m2n</guid>
      <description>&lt;p&gt;Si construyes formularios, onboarding o sistemas de nómina para usuarios en México, tarde o temprano te topas con la CURP. Y la primera reacción de muchos devs es la misma: "le pongo un regex de 18 caracteres alfanuméricos y listo".&lt;/p&gt;

&lt;p&gt;El problema: un regex solo verifica la forma. Una CURP con un error de dedo (una O en lugar de un 0, dos letras intercambiadas) pasa un regex sin problema — y termina en un trámite rechazado. La CURP tiene un mecanismo de autoverificación incorporado: el dígito verificador. Si lo implementas, tu validador detecta la mayoría de errores de captura sin consultar ninguna base de datos.&lt;/p&gt;

&lt;p&gt;La estructura: cada posición significa algo&lt;/p&gt;

&lt;p&gt;La CURP tiene 18 caracteres, en este orden:&lt;/p&gt;

&lt;p&gt;1–4: iniciales — inicial y primera vocal interna del primer apellido, inicial del segundo apellido, inicial del nombre.&lt;br&gt;
5–10: fecha de nacimiento en formato AAMMDD.&lt;br&gt;
11: sexo — H o M (RENAPO también contempla X para personas no binarias).&lt;br&gt;
12–13: clave de la entidad de nacimiento (32 estados; NE = nacido en el extranjero).&lt;br&gt;
14–16: primeras consonantes internas (no iniciales) del primer apellido, segundo apellido y nombre.&lt;br&gt;
17: diferenciador de homonimia — un dígito (0–9) para nacidos antes del 2000, una letra para nacidos a partir del 2000.&lt;br&gt;
18: dígito verificador.&lt;/p&gt;

&lt;p&gt;El dígito verificador: suma ponderada&lt;/p&gt;

&lt;p&gt;Aquí está la parte que hace que la validación valga la pena. Para los primeros 17 caracteres:&lt;/p&gt;

&lt;p&gt;Convierte cada carácter a un número: 0–9 valen 0–9, A–N valen 10–23, Ñ vale 24, O–Z valen 25–36.&lt;br&gt;
Multiplica cada valor por un peso descendente: el primer carácter por 18, el segundo por 17, …, el decimoséptimo por 2.&lt;br&gt;
Suma todo. El dígito verificador es (10 − (suma mod 10)) mod 10.&lt;br&gt;
En JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Mapa de valores: 0–9 → 0–9, A–N → 10–23, Ñ → 24, O–Z → 25–36&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VALOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ñ&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;O&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;digitoVerificador&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;primeros17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;suma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;suma&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;VALOR&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;primeros17&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// pesos 18, 17, …, 2&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suma&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// La CURP es válida si:&lt;/span&gt;
&lt;span class="nf"&gt;digitoVerificador&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cambia un solo carácter y el dígito deja de coincidir. Por eso este chequeo detecta casi cualquier error de captura.&lt;/p&gt;

&lt;p&gt;Los casos borde que rompen los validadores ingenuos&lt;/p&gt;

&lt;p&gt;Nombres con María o José: si el nombre empieza con María o José, se usa el siguiente nombre para la inicial. Un validador que no contempla esto rechazará CURP válidas.&lt;br&gt;
La Ñ: tiene su propio valor (24) en la tabla. Si tu mapa la ignora o la trata como N, el dígito sale mal.&lt;br&gt;
Sin segundo apellido: la posición 3 se llena con X. Lo mismo con las consonantes internas: si no existen, va X.&lt;br&gt;
Nacidos en el extranjero: la entidad es NE.&lt;br&gt;
El diferenciador (posición 17): un validador que exige dígito ahí rechazará a todos los nacidos a partir del 2000, cuya posición 17 es una letra.&lt;br&gt;
Fecha imposible: 31 de febrero, mes 13, etc. Valida que la fecha exista de verdad.&lt;br&gt;
Lo que la validación NO demuestra&lt;/p&gt;

&lt;p&gt;Que la estructura sea válida no significa que la CURP esté registrada en RENAPO ni que pertenezca a quien la presenta. La validación de formato detecta errores de captura, no identidades falsas. Para confirmar el registro hay que consultar el portal oficial gob.mx/curp.&lt;/p&gt;

&lt;p&gt;Si no quieres implementarlo&lt;/p&gt;

&lt;p&gt;Si solo necesitas verificar una CURP sin escribir código, hay un &lt;a href="https://herramatica.com/validar-curp" rel="noopener noreferrer"&gt;Validador de CURP&lt;/a&gt; que aplica todas estas reglas en el navegador: revisa las 18 posiciones, calcula el dígito verificador, te dice exactamente qué posición falla y te muestra los datos que contiene la clave (fecha, sexo, estado, edad). Y como todo corre del lado del cliente, la CURP nunca se envía ni se guarda.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
