Se você já precisou de um formulário de cadastro que aceita CNPJ, muito provavelmente descobriu na marra que dígito verificador não é firula burocrática. É a diferença entre gravar no banco um número que realmente existe e gravar lixo digitado errado que vai quebrar sua integração com a Receita três semanas depois. E agora, em 2026, essa história ganhou um capítulo novo: o CNPJ deixou de ser só número. Passou a aceitar letras. Toda validação que a gente escreveu nos últimos vinte anos precisa de revisão.
Neste post a gente vai do começo: o que é validar um CNPJ de verdade, como funciona o algoritmo do dígito verificador, o passo a passo com os pesos e o módulo 11, e depois a parte que está tirando o sono de muito time de dev, o novo CNPJ alfanumérico. No fim tem código pronto em JavaScript, Python e PHP, já preparado para os dois formatos, além dos erros que a gente vê todo mundo cometendo. Bora.
O que é validar um CNPJ e como o número é estruturado
Validar um CNPJ é conferir, por conta própria e sem consultar nenhum servidor, se aquele número obedece à regra matemática que a Receita Federal usa para gerar CNPJs válidos. Não é checar se a empresa existe (isso é consulta), é só verificar a consistência interna do número. Os dois últimos dígitos, chamados de dígitos verificadores (DV), são calculados a partir dos anteriores. Se você recalcular e bater, o número é formalmente válido. Se não bater, alguém digitou errado ou inventou.
O CNPJ tem 14 posições. A estrutura tradicional é assim:
| Trecho | Posições | Significado |
|---|---|---|
| Raiz (base) | 1 a 8 | Identifica a empresa (a matriz) |
| Ordem | 9 a 12 | Identifica a filial (0001 é a matriz) |
| Dígito verificador | 13 e 14 | Os dois números que a gente calcula e confere |
No formato clássico, escrito como 11.222.333/0001-81, as 12 primeiras posições são só números, e os dois últimos também. A pontuação (pontos, barra e hífen) é cosmética: serve para o olho humano ler, e a primeira coisa que qualquer validação faz é jogar isso fora, deixando só os 14 caracteres que interessam.
Como funciona o dígito verificador
A ideia por trás do DV é velha e elegante. Você pega os dígitos que já tem, multiplica cada um por um peso pré-definido, soma tudo, aplica o módulo 11 e transforma o resto dessa divisão em um dígito. Esse dígito é anexado ao número. Depois, quem recebe faz a mesma conta e confere se o resultado bate. Como o cálculo é determinístico, um único dígito trocado quase sempre muda o resultado, e o erro é pego na hora.
O CNPJ usa dois dígitos verificadores porque um só deixaria passar erros demais. O primeiro DV é calculado sobre as 12 primeiras posições. O segundo DV é calculado sobre as 12 primeiras mais o primeiro DV que a gente acabou de achar, num total de 13 posições. Por isso a ordem importa: você precisa do primeiro para calcular o segundo.
Os pesos são o coração da coisa. Para o primeiro dígito, a Receita usa esta sequência da esquerda para a direita:
| Posição | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Peso (1º DV) | 5 | 4 | 3 | 2 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
Repare no padrão: os pesos descem de 5 até 2, e quando chegariam em 1 pulam de volta para 9 e recomeçam a descida. Para o segundo dígito, como agora são 13 posições, a sequência de pesos ganha um valor a mais na frente e fica assim: 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. É o mesmo raciocínio, só que começando um degrau acima porque tem um número a mais para pesar.

O algoritmo passo a passo do CNPJ numérico
Vamos calcular o DV de um CNPJ de exemplo na mão, porque quando você vê a conta acontecendo o código deixa de ser mágica. Peguei a base 11.222.333/0001, que tem os 12 primeiros dígitos 112223330001, e vou descobrir os dois dígitos verificadores.
Passo 1: primeiro dígito. Multiplico cada um dos 12 dígitos pelo peso correspondente e somo:
| Dígito | 1 | 1 | 2 | 2 | 2 | 3 | 3 | 3 | 0 | 0 | 0 | 1 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Peso | 5 | 4 | 3 | 2 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
| Produto | 5 | 4 | 6 | 4 | 18 | 24 | 21 | 18 | 0 | 0 | 0 | 2 |
A soma dos produtos dá 102. Agora aplico o módulo 11, ou seja, pego o resto da divisão de 102 por 11. Como 11 vezes 9 é 99, sobra resto 3. A regra do DV é: se o resto for menor que 2, o dígito é 0; caso contrário, o dígito é 11 menos o resto. Aqui o resto é 3, então o primeiro DV é 11 menos 3, que dá 8. Já temos o primeiro dígito: 8.
Passo 2: segundo dígito. Agora trabalho com 13 posições: os 12 originais mais o 8 que acabei de calcular, formando 1122233300018. Uso a sequência de pesos de 13 elementos (6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2), multiplico posição a posição e somo. O CNPJ real dessa base é 11.222.333/0001-81, então o segundo dígito é 1. O ponto que importa é o mecanismo: soma ponderada, módulo 11, e a regrinha do “menor que 2 vira 0”. Errar na conta na mão é fácil, e é exatamente por isso que a gente delega isso pro código.
Resumindo o algoritmo em linguagem de gente:
- Limpe o CNPJ, deixando só os 14 caracteres úteis.
- Pegue os 12 primeiros e multiplique cada um pelo seu peso.
- Some, tire o módulo 11, aplique a regra do “menor que 2 vira 0” e ache o primeiro DV.
- Repita com 13 posições (incluindo o primeiro DV) e pesos deslocados para achar o segundo DV.
- Compare os dois DVs calculados com os dois últimos caracteres do CNPJ recebido. Se batem, é válido.
O novo CNPJ alfanumérico e por que isso muda tudo em 2026
Aqui está a novidade que motivou este post inteiro. A Receita Federal, junto com a Serpro e o Comitê Gestor do Simples Nacional, definiu que o CNPJ vai passar a ser alfanumérico. O motivo é prático e um pouco assustador: o estoque de raízes numéricas está acabando. Com só dígitos de 0 a 9 nas 8 posições da raiz, o número de combinações possíveis é finito, e o Brasil abre empresa num ritmo que vai esgotar isso. Colocar letras no jogo multiplica a quantidade de identificadores disponíveis por uma margem gigantesca.
O desenho da mudança foi pensado para doer o mínimo possível. Veja o que muda e o que continua igual:
| Característica | CNPJ numérico (atual) | CNPJ alfanumérico (2026) |
|---|---|---|
| Total de posições | 14 | 14 (não muda) |
| Posições 1 a 12 | Só números (0-9) | Números e letras maiúsculas (0-9, A-Z) |
| Posições 13 e 14 (DV) | Só números | Só números (continuam numéricos) |
| Máscara visual | 00.000.000/0000-00 | Mesma máscara, com letras nas 12 primeiras |
| Cálculo do DV | Usa o valor do dígito | Usa o valor ASCII do caractere menos 48 |
Preste atenção na última linha, porque é aí que mora a mudança que quebra o código antigo. No CNPJ numérico, quando você multiplica um dígito pelo peso, o “valor” do dígito é ele mesmo: o caractere 7 vale 7. No alfanumérico, esse valor passa a ser calculado a partir da tabela ASCII. A fórmula é: pega o código ASCII do caractere e subtrai 48.
Por que 48? Porque na tabela ASCII o caractere '0' tem código 48, o '1' tem 49, e assim por diante até o '9' que tem 57. Subtraindo 48, o '0' vira 0 e o '9' vira 9, exatamente como antes. A beleza disso é que a conta dos números não muda em nada. Agora as letras: 'A' tem código ASCII 65, então vale 65 menos 48, que dá 17. O 'Z' tem código 90, então vale 90 menos 48, que dá 42. Olha a continuidade:
| Caractere | Código ASCII | Valor no cálculo (ASCII – 48) |
|---|---|---|
| ‘0’ | 48 | 0 |
| ‘9’ | 57 | 9 |
| ‘A’ | 65 | 17 |
| ‘B’ | 66 | 18 |
| ‘Z’ | 90 | 42 |
O resto do algoritmo fica idêntico: mesmos pesos (5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 para o primeiro DV, com o 6 na frente para o segundo), mesma soma ponderada, mesmo módulo 11, mesma regra do “menor que 2 vira 0”. Os dígitos verificadores continuam sendo números de 0 a 9, então o resultado final do cálculo não muda de natureza. O que muda é só como você converte cada um dos 12 primeiros caracteres em número antes de multiplicar pelo peso.
E é por isso que todo dev vai ter que rever sua validação. Se o seu código faz parseInt(caractere) ou usa regex tipo /^\d{14}$/ para garantir que o CNPJ é só número, ele vai rejeitar todo CNPJ alfanumérico válido a partir do momento em que a Receita começar a emitir. Empresas com CNPJ novo não vão conseguir se cadastrar no seu sistema, e o suporte vai receber ticket achando que é bug do formulário. A boa notícia: quem escreve a validação usando o truque do ASCII menos 48 desde já cobre os dois mundos com o mesmo código, porque para os dígitos numéricos o resultado é idêntico.

Código de validação: JavaScript, Python e PHP
Chega de teoria. Os três blocos abaixo validam CNPJ e já nascem compatíveis com o formato alfanumérico. A estratégia é a mesma nas três linguagens: limpar a entrada, checar tamanho, rejeitar o caso patológico de todos os caracteres iguais, calcular os dois dígitos com a conversão ASCII menos 48 e comparar. Eu comentei os pontos que costumam gerar dúvida.
JavaScript. Aqui a conversão sai de charCodeAt(0) - 48. A regex de limpeza mantém letras e números.
function validarCNPJ(cnpj) {
// Remove tudo que nao for letra maiuscula ou numero
const limpo = String(cnpj).toUpperCase().replace(/[^0-9A-Z]/g, '');
if (limpo.length !== 14) return false;
// DV precisa ser numerico nas duas ultimas posicoes
if (!/^[0-9]{2}$/.test(limpo.slice(12))) return false;
// Rejeita sequencias repetidas tipo AAAAAAAAAAAA00
if (/^(.)\1{13}$/.test(limpo)) return false;
const valor = (c) => c.charCodeAt(0) - 48;
const calcDV = (base) => {
const pesos = base.length === 12
? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
: [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
let soma = 0;
for (let i = 0; i < base.length; i++) {
soma += valor(base[i]) * pesos[i];
}
const resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
};
const dv1 = calcDV(limpo.slice(0, 12));
const dv2 = calcDV(limpo.slice(0, 12) + dv1);
return dv1 === Number(limpo[12]) && dv2 === Number(limpo[13]);
}
console.log(validarCNPJ('11.222.333/0001-81')); // true
console.log(validarCNPJ('12ABC34501DE35')); // exemplo alfanumerico
Python. Aqui a conversão usa ord(c) - 48, que é o equivalente do charCodeAt. O resto é a mesma receita.
import re
def validar_cnpj(cnpj: str) -> bool:
limpo = re.sub(r'[^0-9A-Z]', '', str(cnpj).upper())
if len(limpo) != 14:
return False
# Os dois digitos verificadores precisam ser numericos
if not limpo[12:].isdigit():
return False
# Rejeita todos os caracteres iguais
if len(set(limpo)) == 1:
return False
def valor(c: str) -> int:
return ord(c) - 48
def calc_dv(base: str) -> int:
if len(base) == 12:
pesos = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
else:
pesos = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
soma = sum(valor(c) * p for c, p in zip(base, pesos))
resto = soma % 11
return 0 if resto < 2 else 11 - resto
dv1 = calc_dv(limpo[:12])
dv2 = calc_dv(limpo[:12] + str(dv1))
return dv1 == int(limpo[12]) and dv2 == int(limpo[13])
print(validar_cnpj('11.222.333/0001-81')) # True
print(validar_cnpj('12ABC34501DE35')) # exemplo alfanumerico
PHP. A função nativa ord() devolve o código ASCII do primeiro byte, então ord($c) - 48 faz o mesmo trabalho. Uso str_split para percorrer caractere a caractere.
<?php
function validarCNPJ($cnpj) {
$limpo = preg_replace('/[^0-9A-Z]/', '', strtoupper((string) $cnpj));
if (strlen($limpo) !== 14) return false;
// Os dois ultimos (DV) tem que ser numericos
if (!ctype_digit(substr($limpo, 12))) return false;
// Rejeita caracteres todos iguais
if (preg_match('/^(.)\1{13}$/', $limpo)) return false;
$valor = fn($c) => ord($c) - 48;
$calcDV = function ($base) use ($valor) {
$pesos = strlen($base) === 12
? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
: [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
$soma = 0;
$chars = str_split($base);
foreach ($chars as $i => $c) {
$soma += $valor($c) * $pesos[$i];
}
$resto = $soma % 11;
return $resto < 2 ? 0 : 11 - $resto;
};
$dv1 = $calcDV(substr($limpo, 0, 12));
$dv2 = $calcDV(substr($limpo, 0, 12) . $dv1);
return $dv1 === (int) $limpo[12] && $dv2 === (int) $limpo[13];
}
var_dump(validarCNPJ('11.222.333/0001-81')); // bool(true)
var_dump(validarCNPJ('12ABC34501DE35')); // exemplo alfanumerico
?>
Nas três versões eu deixei o cálculo do DV numa função separada e passei a base com tamanho variável (12 ou 13). Isso evita duplicar a lógica e deixa claro que a única diferença entre o primeiro e o segundo dígito é o tamanho da base e a lista de pesos. Se você mantiver essa estrutura, adicionar suporte alfanumérico foi de graça: bastou trocar a conversão de dígito por ord/charCodeAt menos 48.
Erros comuns na validação de CNPJ
Depois de ver muita implementação por aí, alguns tropeços aparecem sempre. Vale conhecer para não cair neles.
Confiar só na regex de formato. Um erro clássico é achar que uma regex de máscara valida CNPJ. Ela valida o formato visual, não a matemática. 00.000.000/0000-00 passa nessa regex e é um CNPJ que não existe. Regex confere aparência, o algoritmo confere consistência. Você precisa dos dois.
Esquecer de rejeitar sequências repetidas. Números como 11.111.111/1111-11 passam no cálculo do módulo 11 porque a matemática fecha certinho, mas não são CNPJs válidos. Todo código sério tem uma linha que barra os 14 caracteres iguais antes de calcular. Nos três exemplos acima essa checagem está lá.
Usar tipos numéricos para guardar o CNPJ. CNPJ não é número que você soma, é um identificador. Se você armazenar como inteiro, perde os zeros à esquerda (01.234.567/... vira outra coisa) e, com o alfanumérico chegando, simplesmente não cabe mais em um campo numérico. Guarde como string, sempre.
Presumir que o CNPJ é só dígito no alfanumérico. Esse é o erro do momento. Qualquer isdigit(), is_numeric() ou regex de só números aplicado às 12 primeiras posições vai começar a rejeitar CNPJs válidos em 2026. A validação numérica tem que ficar restrita às duas posições do DV, que continuam sendo dígitos, e nada mais.
Não normalizar a entrada. O usuário cola com pontuação, sem pontuação, com espaço no fim, às vezes com letra minúscula no formato novo. Se você não limpa e não coloca em maiúsculo antes de calcular, um CNPJ perfeitamente válido é reprovado por causa de um espaço. A primeira linha de toda função de validação deveria ser a normalização.
Perguntas frequentes
Validar um CNPJ é o mesmo que consultar se a empresa existe?
Não. Validar é conferir a matemática do dígito verificador localmente, sem internet, e diz apenas que o número é bem formado. Consultar é bater na base da Receita Federal para saber se aquele CNPJ está ativo, qual a razão social, situação cadastral e por aí vai. Na prática você usa os dois em sequência: valida primeiro (rápido e de graça) para não desperdiçar consulta com número digitado errado, e só depois consulta o que passou.
O CNPJ alfanumérico invalida os CNPJs numéricos antigos?
Não, e esse é o ponto de conforto. Todos os CNPJs numéricos existentes continuam válidos para sempre. O alfanumérico vale só para as novas inscrições, quando o estoque numérico se esgotar. Por isso seu sistema precisa aceitar os dois formatos ao mesmo tempo, não trocar um pelo outro. O código que a gente mostrou faz exatamente isso: valida numérico e alfanumérico com a mesma função.
Por que subtrair 48 do código ASCII no cálculo do dígito?
Porque 48 é o código ASCII do caractere '0'. Subtraindo 48, você faz os caracteres de '0' a '9' valerem de 0 a 9, mantendo o cálculo antigo intacto, e ainda dá um valor consistente e contínuo para as letras ('A' vira 17, 'Z' vira 42). É um jeito elegante de estender o algoritmo sem criar uma tabela de conversão manual.
Os dígitos verificadores do CNPJ alfanumérico podem ser letras?
Não. As posições 13 e 14, os dois dígitos verificadores, continuam sendo numéricas de 0 a 9 no formato novo. Só as 12 primeiras posições aceitam letras. Por isso, nos exemplos de código, a checagem de “os dois últimos são dígitos” continua valendo e é uma validação legítima, diferente de exigir que o CNPJ inteiro seja numérico.
Preciso mesmo rejeitar CNPJ com todos os caracteres iguais?
Sim, é uma boa prática consolidada. Sequências como 00000000000000 satisfazem a conta do módulo 11 por coincidência matemática, mas nunca correspondem a uma empresa real. São entradas típicas de teste ou de preenchimento automático malicioso. Barrar todos iguais custa uma linha e evita gravar lixo que parece válido.
Qual a diferença de peso entre o primeiro e o segundo dígito verificador?
O primeiro DV usa 12 pesos: 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. O segundo DV usa 13 pesos, porque agora existe uma posição a mais (o próprio primeiro DV entra na conta), e a sequência ganha um 6 na frente: 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. O padrão de descida e reinício em 9 é o mesmo, só deslocado por causa do tamanho.
Conclusão
Validar CNPJ é um daqueles problemas que parecem triviais até você olhar de perto. A matemática do dígito verificador é simples: soma ponderada, módulo 11, uma regrinha para o resto pequeno. O que a torna robusta é a combinação de dois dígitos e a escolha inteligente de pesos, que pega quase todo erro de digitação. E agora, com o CNPJ alfanumérico entrando em 2026, o algoritmo ganhou uma extensão que, felizmente, é elegante: trocar o valor do dígito pelo código ASCII menos 48 cobre números e letras com a mesma conta.
Se você tirar um recado deste post, que seja este: pare de assumir que CNPJ é só número. Guarde como string, normalize a entrada, use a conversão ASCII menos 48 e restrinja a checagem numérica apenas aos dois dígitos verificadores. Fazendo isso hoje, seu sistema atravessa a virada de 2026 sem rejeitar nenhuma empresa nova. Por aqui, no Hub do Desenvolvedor, a gente mantém uma API de consulta de CNPJ para quando você precisar ir além da validação e buscar os dados reais da empresa, mas a validação em si, essa você já tem tudo para fazer sozinho, direto no seu código.

