Validar CPF: o cálculo e o código em Python e JS
Como validar CPF pelo módulo 11, com código testado em Python e JavaScript, regex de formato, o novo CNPJ alfanumérico e o algoritmo de Luhn.

Um formulário de cadastro aceita 111.111.111-11, o número entra no banco e ninguém repara até aparecerem dezenas de cadastros com ele. O campo tinha máscara, regex e cálculo do dígito verificador. Faltava a regra da sequência repetida.
Quem precisa validar CPF num sistema próprio costuma copiar uma função pronta sem testar esse caso. Este guia mostra o cálculo do módulo 11, o código testado em Python e JavaScript e a regex de formato. No fim, entram CNPJ e cartão.
Validar CPF confere a matemática do número e não diz nada sobre a pessoa por trás dele. A regex, sozinha, confere só o desenho dos caracteres.
Como o CPF é montado
O CPF tem 11 dígitos. Os nove primeiros são a base, e os dois últimos são dígitos verificadores, calculados a partir dessa base. Com máscara, ele fica no formato 000.000.000-00.
O dígito verificador existe para pegar erro de digitação. Se alguém troca um dígito ou inverte dois vizinhos, a conta quase sempre deixa de fechar. Ele não protege nada: qualquer pessoa que conhece a fórmula gera um número que passa.
O exemplo deste post é 123.456.789-09, gerado pela própria conta a partir da base 123456789.
O cálculo para validar CPF, passo a passo
O método é o módulo 11, aplicado duas vezes. A primeira rodada gera o primeiro dígito verificador. A segunda usa a base mais esse dígito e gera o segundo.
Primeiro dígito verificador
Multiplique cada dígito da base por um peso que vai de 10 a 2. Depois some tudo.
1×10 + 2×9 + 3×8 + 4×7 + 5×6 + 6×5 + 7×4 + 8×3 + 9×2 = 210.
Divida a soma por 11 e fique com o resto. 210 dividido por 11 dá 19, com resto 1. A regra é esta: se o resto for menor que 2, o dígito é 0; nos outros casos, o dígito é 11 menos o resto. Com resto 1, o primeiro verificador é 0.
Segundo dígito verificador
Agora a sequência tem dez dígitos, com o 0 no fim. Os pesos vão de 11 a 2.
1×11 + 2×10 + 3×9 + 4×8 + 5×7 + 6×6 + 7×5 + 8×4 + 9×3 + 0×2 = 255.
O resto de 255 por 11 é 2, porque 11 vezes 23 dá 253. Como 2 não é menor que 2, o dígito é 11 menos 2, ou seja, 9. O CPF completo fica 123.456.789-09.
Para validar CPF digitado pelo usuário, você refaz as duas contas sobre os nove primeiros dígitos e compara com os dois últimos. Se bater, o número é matematicamente válido.
A armadilha da sequência repetida
O número 111.111.111-11 passa nas duas contas. O mesmo vale para as outras sequências de um dígito só: rodei as dez no código e todas fecharam. A função precisa rejeitar esses números antes da conta, porque eles aparecem quando alguém quer pular o campo.
O código em Python e em JavaScript
As duas funções abaixo seguem o diagrama para validar CPF. Elas limpam a entrada, conferem o tamanho, rejeitam a sequência repetida e calculam os dois dígitos. Para quem começa em Python, a versão com zip e range é a mais curta.
import re
def validar_cpf(valor: str) -> bool:
digitos = re.sub(r"[^0-9]", "", valor)
if len(digitos) != 11:
return False
if digitos == digitos[0] * 11:
return False
numeros = [int(d) for d in digitos]
for posicao in (9, 10):
pesos = range(posicao + 1, 1, -1)
soma = sum(n * p for n, p in zip(numeros[:posicao], pesos))
resto = soma % 11
dv = 0 if resto < 2 else 11 - resto
if numeros[posicao] != dv:
return False
return TrueA limpeza usa [^0-9] em vez de \D porque, em Python, \d também reconhece dígitos de outros alfabetos.
Estes são os testes que rodei com python3:
casos = {
"123.456.789-09": True,
"12345678909": True,
"123.456.789-08": False,
"111.111.111-11": False,
"000.000.000-00": False,
"123.456.789": False,
"": False,
}
for cpf, esperado in casos.items():
assert validar_cpf(cpf) == esperado, cpf
print("todos os casos passaram")A versão em JavaScript roda igual no navegador e no Node.js:
function validarCpf(valor) {
const digitos = String(valor).replace(/\D/g, "");
if (digitos.length !== 11) return false;
if (/^(\d)\1{10}$/.test(digitos)) return false;
const numeros = [...digitos].map(Number);
for (const posicao of [9, 10]) {
let soma = 0;
for (let i = 0; i < posicao; i++) {
soma += numeros[i] * (posicao + 1 - i);
}
const resto = soma % 11;
const dv = resto < 2 ? 0 : 11 - resto;
if (numeros[posicao] !== dv) return false;
}
return true;
}Os mesmos sete casos, rodados com node, deram o mesmo resultado.
Como as duas funções removem tudo o que não é dígito, elas aceitam lixo como 123abc456.789-09. A regex da próxima seção recusa esse tipo de entrada.
Regex para o formato, com e sem máscara
A expressão mais comum deixa cada separador opcional:
const FORMATO_CPF = /^\d{3}\.?\d{3}\.?\d{3}-?\d{2}$/;Ela recusa 12.3456.789-09 e 123-456-789.09, que têm a pontuação no lugar errado. Mas aceita 123456.789-09, porque cada ponto é opcional por conta própria. Se você quer exigir a máscara inteira ou nenhuma máscara, use a alternância ^(\d{3}\.\d{3}\.\d{3}-\d{2}|\d{11})$, que testei com os mesmos casos.
Nenhuma das duas faz conta. As duas aceitam 123.456.789-08, que tem o desenho certo e o dígito errado, e aceitam 111.111.111-11.
A regex dá retorno rápido ao usuário enquanto ele digita, e a função de dígito verificador decide antes de gravar. Para validar CPF com segurança, as duas rodam também no servidor. Qualquer pessoa consegue chamar a sua API direto, sem passar pelo navegador.
CNPJ alfanumérico e cartão de crédito
O CNPJ tem 14 caracteres: 8 de raiz, 4 de ordem e 2 verificadores. Os pesos vão de 5 a 2 e depois de 9 a 2; no segundo dígito, a sequência ganha um 6 na frente.
A mudança está no formato. A Instrução Normativa RFB nº 2.229, publicada em 15 de outubro de 2024, permite letras no CNPJ, segundo a nota do Serpro. As 12 primeiras posições podem ter letras maiúsculas ou números, e os dois verificadores continuam numéricos. O perguntas e respostas da Receita Federal diz que os primeiros números alfanuméricos entram em produção a partir de julho de 2026. Os CNPJs atuais continuam válidos.
Para o cálculo, cada caractere vira o seu código ASCII menos 48. Os dígitos ficam com o próprio valor, e a letra A vale 17. O exemplo da Receita é 12.ABC.345/01DE. A primeira soma dá 459, com resto 8 e dígito 3; a segunda dá 424, com resto 6 e dígito 5. O resultado é 12.ABC.345/01DE-35.
def dv_cnpj(base12: str) -> str:
valores = [ord(c) - 48 for c in base12.upper()]
pesos = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
for _ in range(2):
soma = sum(v * p for v, p in zip(valores, pesos))
resto = soma % 11
valores.append(0 if resto < 2 else 11 - resto)
pesos.insert(0, 6)
return f"{valores[-2]}{valores[-1]}"A função devolve 35 para 12ABC34501DE e 08 para 12345678000A, outro exemplo do mesmo documento. Neste segundo caso a primeira soma tem resto 1, e o dígito 0 confirma a regra do resto menor que 2. O sistema que guarda CNPJ como número inteiro, ou que valida com \d{14}, vai recusar os novos.
O cartão de crédito usa outro algoritmo, o de Luhn. Hans Peter Luhn criou o algoritmo na IBM, e a patente americana saiu em 1960, segundo a página do algoritmo de Luhn. A norma ISO/IEC 7812-1 o especifica. A conta dobra um dígito sim, outro não, a partir da direita. O dígito dobrado que passar de 9 perde 9, e a soma final tem que ser múltipla de 10. Ele pega todo erro de um dígito só e quase toda inversão de vizinhos, mas não percebe a troca de 09 por 90.
function luhnValido(numero) {
const digitos = String(numero).replace(/\D/g, "");
let soma = 0;
for (let i = 0; i < digitos.length; i++) {
let d = Number(digitos[digitos.length - 1 - i]);
if (i % 2 === 1) {
d *= 2;
if (d > 9) d -= 9;
}
soma += d;
}
return digitos.length > 0 && soma % 10 === 0;
}Com o número de exemplo da mesma página, 17893729974, a função devolve true; trocando o último dígito por 5, devolve false.
| Documento | Tamanho | Algoritmo | Pega o quê |
|---|---|---|---|
| CPF | 11 dígitos | módulo 11, pesos 10 a 2 e 11 a 2 | erro de digitação; sequência repetida exige regra à parte |
| CNPJ numérico | 14 dígitos | módulo 11, pesos 5 a 2 e 9 a 2 | erro de digitação |
| CNPJ alfanumérico | 12 caracteres e 2 dígitos | módulo 11 sobre ASCII menos 48 | erro de digitação, inclusive em letra |
| Cartão de crédito | varia por emissor | Luhn, módulo 10 | dígito trocado e quase toda inversão de vizinhos; não pega 09 por 90 |
| Regex de formato | qualquer | nenhum cálculo | só quantidade de caracteres e pontuação |
Como ferramentas de DLP acham CPF em texto
Ferramentas de DLP precisam achar e validar CPF em e-mail, arquivo e texto colado. Com regex sozinha, o falso positivo dispara: celular com DDD e número de pedido também têm 11 dígitos.
Por isso a detecção costuma usar três camadas. A regex encontra os candidatos. O dígito verificador descarta quase todos os números aleatórios. Num teste meu com um milhão de sequências de 11 dígitos sorteadas, cerca de 1% passou na conta. A terceira camada procura uma palavra próxima, como "CPF" ou "contribuinte", numa janela curta de texto. Só então a ferramenta sobe a confiança do alerta.
O que a validação não responde
Validar CPF não diz se o número tem dono. Também não diz se a pessoa está viva ou se a situação cadastral está regular. Essas respostas vêm da consulta à base da Receita Federal.
O CPF também é dado pessoal, e a LGPD para PMEs trata o que isso exige de quem guarda o número. No código, isso pesa no log: quando a validação falha, registre o motivo (tamanho, sequência repetida ou dígito errado) e deixe o valor de fora.
Checklist para validar CPF num formulário:
- Aceite o número com e sem máscara, e aplique a máscara só na exibição.
- Confira o formato com a regex e remova o que não é dígito.
- Confira se sobraram exatamente 11 dígitos.
- Rejeite as dez sequências repetidas antes do cálculo.
- Calcule os dois dígitos verificadores e compare com os dois últimos.
- Repita a mesma validação no servidor.
- Guarde o CPF como texto, porque número inteiro apaga o zero à esquerda.
- Mantenha o CPF fora do log, da mensagem de erro e da ferramenta de monitoramento.
Cada base de nove dígitos gera um único par de verificadores. Por isso, dos 100 bilhões de combinações de 11 dígitos, só 1 bilhão fecha a conta, e tirando as dez sequências repetidas sobram 999.999.990 números que as funções deste post aceitam.


