Todo formulário brasileiro que pede CPF acaba precisando responder a mesma pergunta: esse número é plausível? A resposta está nos dois últimos dígitos do documento, chamados dígitos verificadores. Eles não são aleatórios — são calculados a partir dos nove primeiros por uma variação do módulo 11. Entender esse cálculo é a diferença entre confiar cegamente numa biblioteca e saber por que uma entrada foi recusada.
Este artigo destrincha o algoritmo dígito a dígito, mostra implementações em JavaScript e Python, lista os casos-limite que costumam passar despercebidos em produção e, mais importante, deixa claro o que a validação sintática não prova.
A estrutura dos onze dígitos
Um CPF tem onze dígitos, normalmente escritos como 000.000.000-00. Os oito primeiros formam o número base da inscrição. O nono identifica a região fiscal onde o documento foi emitido. Os dois últimos são os verificadores, calculados sobre tudo que vem antes.
| Dígito | Região fiscal | Estados |
|---|---|---|
| 0 | 1ª região | DF, GO, MS, MT e TO |
| 1 | 1ª região | DF, GO, MS, MT e TO |
| 2 | 2ª região | AC, AM, AP, PA, RO e RR |
| 3 | 3ª região | CE, MA e PI |
| 4 | 4ª região | AL, PB, PE e RN |
| 5 | 5ª região | BA e SE |
| 6 | 6ª região | MG |
| 7 | 7ª região | ES e RJ |
| 8 | 8ª região | SP |
| 9 | 9ª região | PR e SC |
O cálculo do primeiro dígito verificador
O primeiro verificador usa os nove primeiros dígitos. Cada um é multiplicado por um peso decrescente que começa em 10 e termina em 2. Os produtos são somados, a soma é multiplicada por 10 e o resto da divisão por 11 é o dígito — com uma exceção: se o resto for 10, o dígito é 0.
Vamos acompanhar com o CPF fictício 529.982.247-25, que é o exemplo mais usado em documentação técnica justamente por não ser uma sequência repetida. Os nove primeiros dígitos são 5, 2, 9, 9, 8, 2, 2, 4 e 7.
| Dígito | Peso | Produto |
|---|---|---|
| 5 | 10 | 50 |
| 2 | 9 | 18 |
| 9 | 8 | 72 |
| 9 | 7 | 63 |
| 8 | 6 | 48 |
| 2 | 5 | 10 |
| 2 | 4 | 8 |
| 4 | 3 | 12 |
| 7 | 2 | 14 |
| Soma | — | 295 |
A soma é 295. Multiplicando por 10 chegamos a 2950. O resto de 2950 dividido por 11 é 2. Como o resto é menor que 10, o primeiro dígito verificador é 2 — exatamente o que aparece no documento.
O cálculo do segundo dígito verificador
O segundo verificador repete a mecânica, com duas diferenças: ele considera dez dígitos (os nove originais mais o primeiro verificador que acabamos de calcular) e os pesos começam em 11 em vez de 10.
| Dígito | Peso | Produto |
|---|---|---|
| 5 | 11 | 55 |
| 2 | 10 | 20 |
| 9 | 9 | 81 |
| 9 | 8 | 72 |
| 8 | 7 | 56 |
| 2 | 6 | 12 |
| 2 | 5 | 10 |
| 4 | 4 | 16 |
| 7 | 3 | 21 |
| 2 | 2 | 4 |
| Soma | — | 347 |
347 multiplicado por 10 é 3470, e o resto da divisão por 11 é 5. O segundo dígito verificador é 5. O documento fecha como 529.982.247-25.
Implementação em JavaScript
A implementação abaixo isola o cálculo de um dígito em uma função auxiliar, o que evita duplicar a lógica e deixa o caso do resto 10 num único lugar.
function calcularDigito(digitos, pesoInicial) {
const soma = digitos.reduce(
(acc, digito, indice) => acc + digito * (pesoInicial - indice),
0
);
const resto = (soma * 10) % 11;
return resto === 10 ? 0 : resto;
}
export function validarCpf(entrada) {
const digitos = String(entrada).replace(/\D/g, "");
if (digitos.length !== 11) return false;
// Sequências repetidas satisfazem a fórmula, mas não são documentos válidos.
if (/^(\d)\1{10}$/.test(digitos)) return false;
const numeros = digitos.split("").map(Number);
const base = numeros.slice(0, 9);
const primeiro = calcularDigito(base, 10);
if (primeiro !== numeros[9]) return false;
const segundo = calcularDigito([...base, primeiro], 11);
return segundo === numeros[10];
}Implementação em Python
import re
def calcular_digito(digitos, peso_inicial):
soma = sum(d * (peso_inicial - i) for i, d in enumerate(digitos))
resto = (soma * 10) % 11
return 0 if resto == 10 else resto
def validar_cpf(entrada: str) -> bool:
digitos = re.sub(r"\D", "", str(entrada))
if len(digitos) != 11:
return False
if digitos == digitos[0] * 11:
return False
numeros = [int(c) for c in digitos]
base = numeros[:9]
primeiro = calcular_digito(base, 10)
if primeiro != numeros[9]:
return False
segundo = calcular_digito(base + [primeiro], 11)
return segundo == numeros[10]Casos-limite que derrubam validadores em produção
O algoritmo em si é curto. O que costuma quebrar são as bordas — entradas que chegam de formulários, planilhas, integrações e leitores de documento em formatos que o desenvolvedor não previu.
- 1Sequências repetidas. De 000.000.000-00 a 999.999.999-99, todas passam no módulo 11. Rejeite-as explicitamente antes de calcular, como no exemplo acima.
- 2Zeros à esquerda perdidos. Um CPF lido de planilha como número vira 12345678 em vez de 00012345678. Trate CPF sempre como texto, do parser ao banco.
- 3Pontuação inconsistente. A mesma pessoa pode chegar como 529.982.247-25, 529982247-25 ou 52998224725. Normalize para dígitos puros antes de comparar ou gravar.
- 4Espaços invisíveis. Colagem de PDF costuma trazer espaço não separável (U+00A0). Um replace que só remove \\D resolve, mas um trim simples não.
- 5Tamanho maior que onze. Concatenações acidentais geram doze ou treze dígitos; validar o tamanho depois de limpar evita índices fora do intervalo.
- 6Entrada nula ou indefinida. Converter para texto antes de aplicar a expressão regular impede exceção em campo opcional não preenchido.
O que a validação sintática realmente prova
Esta é a parte mais mal compreendida do assunto. Passar no módulo 11 significa apenas que os onze dígitos são coerentes entre si. Não significa que a inscrição existe, que está ativa, que pertence a quem preencheu o formulário nem que o titular é maior de idade.
| Camada | Pergunta respondida | Onde roda |
|---|---|---|
| Máscara e tamanho | O campo tem o formato esperado? | Frontend |
| Módulo 11 | Os dígitos são internamente consistentes? | Frontend e backend |
| Consulta cadastral | A inscrição existe e está regular? | Serviço oficial autorizado |
| Verificação de identidade | Quem preencheu é o titular? | Provedor de KYC ou processo próprio |
Confundir a segunda linha com a quarta é o erro que leva sistemas a aceitarem cadastros fraudulentos achando que estão protegidos. Validação sintática é filtro de digitação, não controle antifraude.
Gerando CPFs para teste sem usar dados reais
Justamente porque o algoritmo é determinístico, é trivial gerar números que passam nele. Isso resolve um problema real de equipes de desenvolvimento e QA: preencher massa de teste sem copiar documentos de pessoas físicas para bases de homologação, o que criaria uma exposição desnecessária sob a LGPD.
A geração é o caminho inverso da validação: sorteia-se os nove primeiros dígitos e calcula-se os dois verificadores com as mesmas funções. É exatamente o que o gerador de CPF do Codigio Labs faz, inteiramente no navegador — nenhum número gerado é enviado a um servidor.
Uma suíte de testes mínima
Se você acabou de escrever seu validador, estes seis casos cobrem quase todo o espaço de falha comum:
describe("validarCpf", () => {
it("aceita CPF válido com pontuação", () => {
expect(validarCpf("529.982.247-25")).toBe(true);
});
it("aceita CPF válido sem pontuação", () => {
expect(validarCpf("52998224725")).toBe(true);
});
it("rejeita dígito verificador incorreto", () => {
expect(validarCpf("529.982.247-26")).toBe(false);
});
it("rejeita sequência repetida", () => {
expect(validarCpf("111.111.111-11")).toBe(false);
});
it("rejeita tamanho diferente de onze dígitos", () => {
expect(validarCpf("5299822472")).toBe(false);
});
it("não lança exceção com entrada vazia ou nula", () => {
expect(validarCpf("")).toBe(false);
expect(validarCpf(null)).toBe(false);
});
});Resumo
- Os dois dígitos finais do CPF vêm de um módulo 11 sobre os dígitos anteriores, com pesos decrescentes a partir de 10 e de 11.
- Resto igual a 10 vira dígito 0 — é o caso-limite mais esquecido em implementações caseiras.
- Sequências repetidas passam na fórmula e precisam de rejeição explícita.
- Armazene sempre os onze dígitos sem pontuação e formate apenas na exibição.
- Validação sintática filtra erro de digitação; ela não confirma existência, titularidade nem regularidade da inscrição.