Como validar CPF e CNPJ em JavaScript, PHP e Python (código pronto)
Validar CPF e CNPJ é uma das primeiras coisas que um formulário de cadastro precisa fazer, e também uma das que mais geram código copiado sem entendimento. O algoritmo dos dígitos verificadores é curto, determinístico e o mesmo há décadas; dominá-lo leva alguns minutos e evita aceitar lixo no banco de dados.
Há, porém, uma confusão frequente: validar não é consultar. A validação diz que o número é bem formado; a consulta diz se ele existe na Receita Federal, se está ativo e a quem pertence. São etapas diferentes, com custos e usos diferentes, e este guia trata das duas.
Você encontra aqui a explicação do cálculo, funções prontas em JavaScript, PHP e Python (já preparadas para o CNPJ alfanumérico), um conjunto de testes e um exemplo de consulta real por API para quando o cadastro exige mais do que dígitos corretos.
Validar não é consultar: dígitos verificadores x situação cadastral
Os dois últimos dígitos de um CPF ou CNPJ são dígitos verificadores, calculados a partir dos anteriores. Validar significa refazer esse cálculo e comparar o resultado com o que o usuário digitou. É uma operação local, instantânea e gratuita, que pega erros de digitação e números inventados sem critério.
Consultar significa perguntar a uma base oficial o que existe por trás do número: se o CNPJ está ativo, baixado ou suspenso, qual é a razão social, quem são os sócios, qual é o endereço. Para CPF, a consulta envolve dados pessoais e só deve acontecer com base legal e finalidade clara, conforme a LGPD.
| Validação | Consulta | |
|---|---|---|
| O que responde | O número é bem formado? | O número existe e qual é a situação? |
| Onde roda | No seu código (front e back) | Em uma API externa |
| Custo | Zero | Por consulta ou por plano |
| Quando usar | Sempre, antes de qualquer coisa | Quando a decisão depende do cadastro real |
A ordem certa é validar primeiro e consultar só o que passou. Além de economizar créditos, isso evita mandar para a API números que nunca retornariam nada.
Como funciona o cálculo dos dígitos verificadores
CPF e CNPJ usam a mesma ideia, o módulo 11: cada caractere da base é multiplicado por um peso, os produtos são somados, e o resto da divisão da soma por 11 define o dígito.
CPF: 9 dígitos de base + 2 verificadores
- Multiplique os 9 primeiros dígitos pelos pesos 10, 9, 8, 7, 6, 5, 4, 3, 2 e some os resultados.
- Calcule
resto = soma % 11. Se o resto for menor que 2, o dígito é 0; caso contrário, é11 - resto. - Repita com os 10 primeiros dígitos (base + primeiro verificador) e os pesos de 11 a 2 para obter o segundo verificador.
Exemplo com o CPF 111.444.777-35: a primeira soma é 162, o resto é 8 e o dígito é 3. A segunda soma, agora com o 3 incluído, é 204, o resto é 6 e o dígito é 5. Os verificadores batem, então o número é válido.
CNPJ: 12 caracteres de base + 2 verificadores
- Multiplique os 12 primeiros caracteres pelos pesos 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 e some.
- Aplique a mesma regra do resto para obter o primeiro verificador.
- Repita com os 13 primeiros caracteres e os pesos 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2.
Uma novidade importante: a Receita Federal definiu o CNPJ alfanumérico, em que as 12 primeiras posições podem conter letras e os dois verificadores continuam numéricos. O cálculo é o mesmo, com uma troca: em vez de usar o valor do dígito, usa-se o código ASCII do caractere menos 48.
Para “0” a “9” o resultado é o próprio dígito (0 a 9), e para “A” a “Z” vai de 17 a 42. As funções abaixo já aplicam essa regra, então funcionam para os dois formatos.
Há ainda um caso que a aritmética não pega: sequências repetidas como 111.111.111-11 ou 00.000.000/0000-00 produzem verificadores corretos, mas não são números emitidos. Toda função séria rejeita essas sequências explicitamente.
Função para validar CPF e CNPJ em JavaScript
O módulo abaixo roda no navegador e no Node.js. A sanitização remove tudo o que não é dígito (ou letra, no caso do CNPJ), para aceitar entradas com ou sem máscara:
// documentos.js
const apenasDigitos = (valor) => String(valor).replace(/\D/g, '');
// Valor de cada caractere: código ASCII - 48 ('0'..'9' => 0..9, 'A'..'Z' => 17..42)
const valorChar = (c) => c.charCodeAt(0) - 48;
function calcularDigito(base, pesos) {
const soma = base.split('').reduce((acc, c, i) => acc + valorChar(c) * pesos[i], 0);
const resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
const pesosDecrescentes = (inicio) => Array.from({ length: inicio - 1 }, (_, i) => inicio - i);
const PESOS_CNPJ = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
export function validarCPF(valor) {
const cpf = apenasDigitos(valor);
if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false; // tamanho e sequências repetidas
const dv1 = calcularDigito(cpf.slice(0, 9), pesosDecrescentes(10));
const dv2 = calcularDigito(cpf.slice(0, 10), pesosDecrescentes(11));
return cpf.endsWith(`${dv1}${dv2}`);
}
export function validarCNPJ(valor) {
const cnpj = String(valor).toUpperCase().replace(/[^A-Z0-9]/g, '');
if (!/^[A-Z0-9]{12}\d{2}$/.test(cnpj) || /^(.)\1{13}$/.test(cnpj)) return false;
const dv1 = calcularDigito(cnpj.slice(0, 12), PESOS_CNPJ.slice(1));
const dv2 = calcularDigito(cnpj.slice(0, 13), PESOS_CNPJ);
return cnpj.endsWith(`${dv1}${dv2}`);
}
Repare que pesosDecrescentes(10) gera [10, 9, ..., 2] e PESOS_CNPJ.slice(1) descarta o 6 inicial, deixando exatamente os pesos do primeiro verificador do CNPJ. A comparação final com endsWith confere os dois dígitos de uma vez.

As mesmas funções em PHP e Python
A lógica não muda de linguagem para linguagem; muda só a sintaxe. Nos dois casos, a função recebe a string com ou sem máscara e devolve um booleano.
PHP
<?php
function validarCPF(string $valor): bool
{
$cpf = preg_replace('/\D/', '', $valor);
if (strlen($cpf) !== 11 || preg_match('/^(\d)\1{10}$/', $cpf)) {
return false;
}
foreach ([9, 10] as $tamanho) { // 9 dígitos para o 1º DV, 10 para o 2º
$soma = 0;
for ($i = 0; $i < $tamanho; $i++) {
$soma += (int) $cpf[$i] * ($tamanho + 1 - $i);
}
$resto = $soma % 11;
$digito = $resto < 2 ? 0 : 11 - $resto;
if ((int) $cpf[$tamanho] !== $digito) {
return false;
}
}
return true;
}
function validarCNPJ(string $valor): bool
{
$cnpj = preg_replace('/[^A-Z0-9]/', '', strtoupper($valor));
if (!preg_match('/^[A-Z0-9]{12}\d{2}$/', $cnpj) || preg_match('/^(.)\1{13}$/', $cnpj)) {
return false;
}
$pesos = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
foreach ([12, 13] as $tamanho) {
$inicio = 13 - $tamanho; // 1 para o 1º DV (pula o 6), 0 para o 2º
$soma = 0;
for ($i = 0; $i < $tamanho; $i++) {
$soma += (ord($cnpj[$i]) - 48) * $pesos[$inicio + $i];
}
$resto = $soma % 11;
$digito = $resto < 2 ? 0 : 11 - $resto;
if ((int) $cnpj[$tamanho] !== $digito) {
return false;
}
}
return true;
}
Python
Em Python, uma função auxiliar com zip deixa o cálculo em três linhas. Se você está começando na linguagem, o tutorial de Python 3 para iniciantes cobre a sintaxe usada aqui.
# documentos.py
import re
PESOS_CNPJ = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
def _digito(base: str, pesos: list[int]) -> int:
soma = sum((ord(c) - 48) * p for c, p in zip(base, pesos))
resto = soma % 11
return 0 if resto < 2 else 11 - resto
def validar_cpf(valor: str) -> bool:
cpf = re.sub(r"\D", "", str(valor))
if len(cpf) != 11 or cpf == cpf[0] * 11:
return False
dv1 = _digito(cpf[:9], list(range(10, 1, -1))) # pesos 10..2
dv2 = _digito(cpf[:10], list(range(11, 1, -1))) # pesos 11..2
return cpf[-2:] == f"{dv1}{dv2}"
def validar_cnpj(valor: str) -> bool:
cnpj = re.sub(r"[^A-Z0-9]", "", str(valor).upper())
if not re.fullmatch(r"[A-Z0-9]{12}\d{2}", cnpj) or cnpj == cnpj[0] * 14:
return False
dv1 = _digito(cnpj[:12], PESOS_CNPJ[1:])
dv2 = _digito(cnpj[:13], PESOS_CNPJ)
return cnpj[-2:] == f"{dv1}{dv2}"
Máscara, sanitização e testes
Três decisões de projeto evitam a maior parte dos bugs em torno dessas funções:
- Sanitize na entrada, formate na saída. Guarde no banco apenas os caracteres significativos (
11144477735) e aplique a máscara só na hora de exibir. Assim, busca e comparação não dependem de como o usuário digitou. - Valide no front-end e no back-end. No navegador, a validação dá feedback imediato; no servidor, ela é a garantia real, porque qualquer requisição pode ser montada à mão.
- Use
inputmode="numeric"no campo de CPF para abrir o teclado numérico no celular, e lembre que o campo de CNPJ precisa aceitar letras.
Para a máscara, duas expressões regulares resolvem:
export const formatarCPF = (cpf) =>
apenasDigitos(cpf).replace(/^(\d{3})(\d{3})(\d{3})(\d{2})$/, '$1.$2.$3-$4');
export const formatarCNPJ = (cnpj) =>
cnpj.toUpperCase().replace(/[^A-Z0-9]/g, '').replace(/^(.{2})(.{3})(.{3})(.{4})(\d{2})$/, '$1.$2.$3/$4-$5');
Antes de confiar na função para validar CPF e CNPJ em produção, teste com casos válidos e inválidos. O arquivo abaixo usa o node:test, que vem com o Node.js, e roda com node --test:
// documentos.test.js
import test from 'node:test';
import assert from 'node:assert/strict';
import { validarCPF, validarCNPJ } from './documentos.js';
test('valida CPF', () => {
assert.equal(validarCPF('111.444.777-35'), true);
assert.equal(validarCPF('11144477735'), true); // sem máscara
assert.equal(validarCPF('111.111.111-11'), false); // sequência repetida
assert.equal(validarCPF('111.444.777-36'), false); // dígito verificador errado
assert.equal(validarCPF('1234'), false); // tamanho inválido
});
test('valida CNPJ', () => {
assert.equal(validarCNPJ('44.959.669/0001-80'), true);
assert.equal(validarCNPJ('12.ABC.345/01DE-35'), true); // formato alfanumérico
assert.equal(validarCNPJ('11.111.111/1111-11'), false);
assert.equal(validarCNPJ('44.959.669/0001-81'), false);
});
Os mesmos casos servem para o PHPUnit e o pytest. Inclua sempre um número com máscara, um sem, uma sequência repetida e um com o último dígito alterado: são os quatro cenários que pegam erros de implementação.
Quando usar uma API de consulta
A validação aprova qualquer número bem formado, inclusive um CNPJ de empresa baixada há anos ou um CPF que não pertence a quem está digitando. Quando a decisão depende disso (abrir conta, liberar crédito, emitir nota, aceitar um fornecedor), entra a consulta.
Para CNPJ, o retorno típico traz razão social, situação cadastral, CNAE, endereço e quadro societário. O exemplo abaixo usa a APIBrasil, que recebe um POST com Bearer Token e devolve um envelope com error, message e data. O guia de como consultar CNPJ na Receita com Python aprofunda cada campo da resposta.
import os
import requests
from documentos import validar_cnpj
def consultar_cnpj(cnpj: str) -> dict:
if not validar_cnpj(cnpj):
raise ValueError("CNPJ inválido: não vale gastar uma consulta")
resposta = requests.post(
"https://gateway.apibrasil.io/api/v2/dados/cnpj/credits",
headers={"Authorization": f"Bearer {os.environ['APIBRASIL_TOKEN']}"},
json={"tipo": "cnpj", "cnpj": cnpj},
timeout=15,
)
corpo = resposta.json()
if not resposta.ok or corpo.get("error"):
raise RuntimeError(corpo.get("message") or f"HTTP {resposta.status_code}")
return corpo["data"]["cnpj"]
empresa = consultar_cnpj("44.959.669/0001-80")
print(empresa["empresa"]["razao_social"], "- situação:", empresa["situacao_cadastral"])
O campo situacao_cadastral segue os códigos da Receita, em que 02 significa ativa. Trate os demais (baixada, suspensa, inapta) de acordo com a regra do seu negócio, e registre a data da consulta: a situação muda com o tempo. O padrão de chamada é o mesmo de outras consultas, como a de placa de veículo por API, então um único cliente HTTP atende todas.
Para CPF, pense no cadastro como um todo. Além de exigir base legal para consultar dados pessoais, a consulta não prova que a pessoa na frente da tela é a dona do documento. Um cadastro confiável de ponta a ponta combina a validação do CPF com uma prova de posse, como verificar o telefone do usuário no cadastro por um código enviado via SMS ou WhatsApp.
Perguntas frequentes
Um CPF válido significa que ele existe?
Não. Válido significa apenas que os dígitos verificadores conferem. É possível gerar milhões de números que passam no algoritmo e nunca foram emitidos. Existência, titularidade e situação só vêm de uma consulta com base legal.
Por que rejeitar sequências como 111.111.111-11?
Porque elas passam na conta do módulo 11, mas não são documentos emitidos, e aparecem com frequência em cadastros falsos ou testes esquecidos em produção. A checagem custa uma expressão regular e elimina os casos mais óbvios.
Devo validar no front-end ou no back-end?
Nos dois. O front-end melhora a experiência com feedback imediato; o back-end é a única camada que você controla de verdade, já que qualquer requisição pode ser enviada sem passar pela tela.
Como validar o CNPJ alfanumérico?
Com o mesmo algoritmo, usando o código ASCII do caractere menos 48 como valor na multiplicação. As funções deste artigo já fazem isso e aceitam tanto o formato numérico quanto o alfanumérico; só os dois verificadores precisam ser dígitos.
Dá para validar CPF só com expressão regular?
Uma regex verifica o formato (pontos, traço, quantidade de dígitos), não os verificadores. Use regex para o formato e o algoritmo para a validação. Sem o cálculo, 123.456.789-00 seria aceito.
Próximos passos
Com as funções deste guia, validar CPF e CNPJ vira um módulo testado que você reaproveita em qualquer projeto, em JavaScript, PHP ou Python. Coloque-o na camada de validação do seu back-end, repita a checagem no front-end para o usuário e deixe a consulta por API para os fluxos em que a situação cadastral decide algo. Se ainda não testou com o formato alfanumérico, adicione o caso ao seu conjunto de testes hoje.