# Como integrar validação de CPF em formulários HTML com JavaScript puro

> Tutorial completo para integrar validação de CPF em formulários HTML usando JavaScript puro, com formatação automática e consulta à API.

**Publicado:** 20/06/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-formularios-html-com-javascript-puro

---


Para integrar validação de CPF em formulários HTML com JavaScript puro, combine três camadas: máscara de formatação automática no campo, validação local dos dígitos verificadores (algoritmo módulo 11) e consulta à API via backend para confirmar que o CPF existe. Nenhuma biblioteca externa é necessária — o código funciona em qualquer navegador moderno sem dependências.

## Introdução

A validação de CPF em formulários HTML é uma das implementações mais comuns no desenvolvimento web brasileiro. Seja em cadastros de e-commerce, sistemas de atendimento, plataformas de serviços ou aplicações internas, o CPF é um campo presente em praticamente todo formulário voltado para o mercado nacional.

Implementar essa validação com JavaScript puro -- sem dependência de frameworks ou bibliotecas externas -- garante compatibilidade universal, performance máxima e controle total sobre o comportamento do campo. Combinada com uma consulta à API da [**CPFHub.io**](https://www.cpfhub.io/), a solução confirma que o CPF informado é real e retorna os dados do titular para cruzamento de identidade.

---
## Estrutura do formulário HTML

O ponto de partida é um formulário HTML bem estruturado com os atributos corretos para campos de CPF:

```html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Cadastro com Validação de CPF</title>
 <style>
 * { margin: 0; padding: 0; box-sizing: border-box; }
 body {
 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
 background: #f0f2f5;
 padding: 40px 20px;
 }
 .form-container {
 max-width: 480px;
 margin: 0 auto;
 background: white;
 padding: 32px;
 border-radius: 8px;
 box-shadow: 0 2px 12px rgba(0,0,0,0.08);
 }
 h1 { font-size: 22px; margin-bottom: 24px; color: #1a1a2e; }
 .form-group { margin-bottom: 20px; }
 label {
 display: block;
 font-size: 14px;
 font-weight: 600;
 color: #374151;
 margin-bottom: 6px;
 }
 input {
 width: 100%;
 padding: 12px 14px;
 border: 2px solid #e5e7eb;
 border-radius: 6px;
 font-size: 16px;
 transition: border-color 0.2s;
 }
 input:focus { outline: none; border-color: #3b82f6; }
 input.valido { border-color: #16a34a; }
 input.invalido { border-color: #ef4444; }
 .mensagem {
 font-size: 13px;
 margin-top: 4px;
 min-height: 18px;
 }
 .mensagem.sucesso { color: #16a34a; }
 .mensagem.erro { color: #ef4444; }
 .mensagem.info { color: #6b7280; }
 button {
 width: 100%;
 padding: 14px;
 background: #3b82f6;
 color: white;
 border: none;
 border-radius: 6px;
 font-size: 16px;
 font-weight: 600;
 cursor: pointer;
 margin-top: 8px;
 }
 button:hover { background: #2563eb; }
 button:disabled { background: #93c5fd; cursor: not-allowed; }
 .resultado {
 margin-top: 20px;
 padding: 16px;
 background: #f0fdf4;
 border: 1px solid #bbf7d0;
 border-radius: 6px;
 display: none;
 }
 .resultado p { font-size: 14px; color: #166534; margin-bottom: 4px; }
 </style>
</head>
<body>
 <div class="form-container">
 <h1>Cadastro</h1>
 <form id="formCadastro" novalidate>
 <div class="form-group">
 <label for="nome">Nome completo</label>
 <input type="text" id="nome" name="nome" placeholder="Seu nome completo"
 required autocomplete="name" />
 </div>
 <div class="form-group">
 <label for="cpf">CPF</label>
 <input type="text" id="cpf" name="cpf" inputmode="numeric"
 pattern="[0-9]*" maxlength="14" placeholder="000.000.000-00"
 required autocomplete="off" />
 <div id="cpfMensagem" class="mensagem"></div>
 </div>
 <div class="form-group">
 <label for="email">E-mail</label>
 <input type="email" id="email" name="email"
 placeholder="seu@email.com" required autocomplete="email" />
 </div>
 <button type="submit" id="btnSubmit">Cadastrar</button>
 </form>
 <div id="resultado" class="resultado">
 <p id="resultadoNome"></p>
 <p id="resultadoCPF"></p>
 </div>
 </div>

 <script src="cpf-validacao.js"></script>
</body>
</html>
```

Pontos importantes do HTML:

* `inputmode="numeric"` -- Exibe o teclado numérico em dispositivos móveis.
* `pattern="[0-9]*"` -- Reforça a validação de formato no lado do cliente.
* `maxlength="14"` -- Limita o campo ao tamanho do CPF formatado (000.000.000-00).
* `autocomplete="off"` -- Evita autopreenchimento indesejado para o campo de CPF.

---

## Máscara de formatação automática

A máscara formata o CPF enquanto o usuário digita, aplicando automaticamente os pontos e o hífen:

```javascript
// cpf-validacao.js

/**
 * Aplica máscara de formatação ao campo de CPF.
 * Formato: 000.000.000-00
 */
function aplicarMascaraCPF(input) {
 input.addEventListener('input', function () {
 let valor = this.value.replace(/\D/g, '');
 valor = valor.slice(0, 11);

 if (valor.length > 9) {
 valor = valor.replace(
 /(\d{3})(\d{3})(\d{3})(\d{1,2})/,
 '$1.$2.$3-$4'
 );
 } else if (valor.length > 6) {
 valor = valor.replace(
 /(\d{3})(\d{3})(\d{1,3})/,
 '$1.$2.$3'
 );
 } else if (valor.length > 3) {
 valor = valor.replace(
 /(\d{3})(\d{1,3})/,
 '$1.$2'
 );
 }

 this.value = valor;
 });
}
```

A máscara funciona removendo todos os caracteres não numéricos, limitando a 11 dígitos e aplicando a formatação conforme o número de dígitos digitados.

---

## Validação dos dígitos verificadores

O CPF possui dois dígitos verificadores (10o e 11o dígitos) calculados pelo algoritmo módulo 11. A validação local verifica se esses dígitos estão corretos:

```javascript
/**
 * Valida os dígitos verificadores do CPF.
 * Retorna true se o CPF é matematicamente válido.
 */
function validarCPFLocal(cpf) {
 // Remover caracteres não numéricos
 cpf = cpf.replace(/\D/g, '');

 // Verificar se tem 11 dígitos
 if (cpf.length !== 11) {
 return false;
 }

 // Rejeitar CPFs com todos os dígitos iguais
 if (/^(\d)\1{10}$/.test(cpf)) {
 return false;
 }

 // Calcular dígitos verificadores
 for (let t = 9; t < 11; t++) {
 let soma = 0;
 for (let i = 0; i < t; i++) {
 soma += parseInt(cpf.charAt(i)) * ((t + 1) - i);
 }
 let digito = ((soma * 10) % 11) % 10;
 if (digito !== parseInt(cpf.charAt(t))) {
 return false;
 }
 }

 return true;
}
```

Essa validação é instantânea e não requer chamada à API. Ela elimina CPFs com formato inválido antes de consumir consultas do seu plano.

---

## Feedback visual em tempo real

Combine a máscara e a validação para fornecer feedback visual imediato ao usuário:

```javascript
/**
 * Configura validação em tempo real com feedback visual.
 */
function configurarValidacaoCPF(inputId, mensagemId) {
 const input = document.getElementById(inputId);
 const mensagem = document.getElementById(mensagemId);

 // Aplicar máscara
 aplicarMascaraCPF(input);

 // Validar após cada entrada
 input.addEventListener('input', function () {
 const digitos = this.value.replace(/\D/g, '');

 if (digitos.length < 11) {
 // CPF incompleto
 this.className = '';
 mensagem.textContent = '';
 mensagem.className = 'mensagem';
 return;
 }

 if (validarCPFLocal(digitos)) {
 // CPF com formato válido
 this.className = 'valido';
 mensagem.textContent = 'CPF com formato válido';
 mensagem.className = 'mensagem sucesso';
 } else {
 // CPF com formato inválido
 this.className = 'invalido';
 mensagem.textContent = 'CPF inválido. Verifique os dígitos.';
 mensagem.className = 'mensagem erro';
 }
 });
}
```

---

## Consulta à API via backend

A validação local verifica apenas se o formato matemático está correto. Para confirmar que o CPF existe e obter os dados do titular, é necessário consultar a API da [**CPFHub.io**](https://www.cpfhub.io/) via backend — nunca expondo a chave de API no front-end.

A chave de API nunca deve ser exposta no front-end. Sempre faça a chamada via backend:

### Backend em Node.js (Express)

```javascript
const express = require('express');
const app = express();

app.get('/api/validar-cpf/:cpf', async (req, res) => {
 const { cpf } = req.params;

 try {
 const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
 method: 'GET',
 headers: {
 'x-api-key': process.env.CPFHUB_API_KEY,
 'Accept': 'application/json'
 },
 timeout: 10000
 });

 const data = await response.json();
 res.json(data);
 } catch (error) {
 res.status(500).json({ success: false, error: 'Erro na consulta' });
 }
});

app.listen(3000);
```

### Chamada à API no front-end (via backend)

```javascript
/**
 * Consulta a API (via backend) para verificar os dados do CPF.
 */
async function verificarCPFAPI(cpf) {
 try {
 const response = await fetch(`/api/validar-cpf/${cpf}`, {
 method: 'GET',
 headers: { 'Accept': 'application/json' }
 });

 if (!response.ok) {
 throw new Error(`HTTP ${response.status}`);
 }

 return await response.json();
 } catch (error) {
 console.error('Erro na verificação:', error);
 return { success: false, error: error.message };
 }
}
```

---

## Integrando tudo no formulário

Agora vamos conectar todas as peças -- máscara, validação local, consulta à API e envio do formulário:

```javascript
document.addEventListener('DOMContentLoaded', function () {
 // Configurar campo de CPF
 configurarValidacaoCPF('cpf', 'cpfMensagem');

 // Interceptar envio do formulário
 const form = document.getElementById('formCadastro');
 const btnSubmit = document.getElementById('btnSubmit');
 const resultado = document.getElementById('resultado');

 form.addEventListener('submit', async function (event) {
 event.preventDefault();

 const cpfInput = document.getElementById('cpf');
 const nomeInput = document.getElementById('nome');
 const mensagem = document.getElementById('cpfMensagem');
 const cpf = cpfInput.value.replace(/\D/g, '');

 // Validação local
 if (!validarCPFLocal(cpf)) {
 cpfInput.className = 'invalido';
 mensagem.textContent = 'CPF inválido. Verifique os dígitos.';
 mensagem.className = 'mensagem erro';
 return;
 }

 // Desabilitar botão durante a consulta
 btnSubmit.disabled = true;
 btnSubmit.textContent = 'Verificando...';
 mensagem.textContent = 'Consultando dados...';
 mensagem.className = 'mensagem info';

 // Consultar API via backend
 const data = await verificarCPFAPI(cpf);

 if (data.success) {
 cpfInput.className = 'valido';
 mensagem.textContent = 'CPF verificado com sucesso!';
 mensagem.className = 'mensagem sucesso';

 // Exibir resultado
 document.getElementById('resultadoNome').textContent =
 'Nome: ' + data.data.name;
 document.getElementById('resultadoCPF').textContent =
 'CPF: ' + cpfInput.value;
 resultado.style.display = 'block';

 // Aqui você pode prosseguir com o cadastro
 // enviarCadastro({ cpf, nome: nomeInput.value, ... });
 } else {
 cpfInput.className = 'invalido';
 mensagem.textContent = 'CPF não encontrado. Verifique o número.';
 mensagem.className = 'mensagem erro';
 resultado.style.display = 'none';
 }

 btnSubmit.disabled = false;
 btnSubmit.textContent = 'Cadastrar';
 });
});
```

---

## Exemplo completo com cURL

Para testar a API diretamente, sem front-end:

```bash
curl -X GET https://api.cpfhub.io/cpf/12345678900 \
 -H "x-api-key: SUA_CHAVE_DE_API" \
 -H "Accept: application/json"
```

Resposta:

```json
{
 "success": true,
 "data": {
 "cpf": "12345678900",
 "name": "João da Silva",
 "nameUpper": "JOAO DA SILVA",
 "gender": "M",
 "birthDate": "15/06/1990",
 "day": 15,
 "month": 6,
 "year": 1990
 }
}
```

---

## Boas práticas de segurança

### Nunca exponha a API key no front-end

A chave de API deve estar apenas no servidor. O front-end chama o backend, que por sua vez chama a API da CPFHub.io.

### Implemente rate limiting no seu backend

Para evitar abusos, limite o número de consultas que um único IP ou sessão pode fazer por minuto:

```javascript
// Exemplo simples de rate limiting
const limitePorIP = {};

function verificarRateLimit(ip) {
 const agora = Date.now();
 if (!limitePorIP[ip]) {
 limitePorIP[ip] = { contagem: 1, inicio: agora };
 return true;
 }

 if (agora - limitePorIP[ip].inicio > 60000) {
 limitePorIP[ip] = { contagem: 1, inicio: agora };
 return true;
 }

 limitePorIP[ip].contagem++;
 return limitePorIP[ip].contagem <= 10; // Máximo 10 consultas por minuto
}
```

### Valide no servidor também

Nunca confie apenas na validação do front-end. O servidor deve validar o formato do CPF antes de chamar a API, pois requisições podem ser enviadas diretamente ao backend sem passar pelo front-end.

### Sanitize os dados

Remova caracteres não numéricos do CPF antes de enviar à API e antes de armazenar no banco de dados. Para orientações de segurança mais amplas, o [OWASP](https://owasp.org) mantém guias sobre validação e sanitização de entrada de dados.

---

## Compatibilidade de navegadores

A solução apresentada utiliza apenas JavaScript vanilla (puro) e é compatível com todos os navegadores modernos:

| Navegador | Versão mínima |
| --- | --- |
| Chrome | 55+ |
| Firefox | 52+ |
| Safari | 11+ |
| Edge | 79+ |
| Opera | 42+ |
| Samsung Internet | 6.2+ |

Para navegadores mais antigos que não suportam `fetch`, utilize `XMLHttpRequest` como fallback ou um polyfill.

---

## Perguntas frequentes

### Por que fazer a validação local antes de chamar a API?
A validação dos dígitos verificadores é instantânea e gratuita. Ela elimina CPFs matematicamente inválidos (digitados errado, sequências como 111.111.111-11) antes de consumir uma consulta do seu plano. A regra prática é: valide localmente primeiro, só chame a API quando o formato estiver correto.

### A chave de API pode ser usada diretamente no JavaScript do front-end?
Não. Qualquer pessoa que inspecionar o código-fonte ou o tráfego de rede encontraria a chave e poderia consumi-la em nome da sua conta. Sempre faça a chamada à API pelo servidor, usando variáveis de ambiente para armazenar a chave (`process.env.CPFHUB_API_KEY` no Node.js).

### O que fazer quando a API retorna `success: false`?
Exiba uma mensagem pedindo que o usuário verifique o número digitado. Não informe se o CPF "não existe" — isso pode ser explorado para enumerar CPFs válidos. Registre o evento no backend para monitorar tentativas suspeitas e aplique rate limiting por IP para dificultar ataques de força bruta.

### Como adaptar essa solução para frameworks como React ou Vue?
A lógica de validação local (`validarCPFLocal`) e a máscara (`aplicarMascaraCPF`) funcionam como funções puras e podem ser importadas em qualquer componente. A chamada à API continua via backend — em React, por exemplo, use `useEffect` com debounce para disparar a consulta quando o CPF atingir 11 dígitos válidos.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)

---

## Conclusão

Integrar a validação de CPF em formulários HTML com JavaScript puro é uma solução leve, performática e universalmente compatível. A combinação de máscara de formatação automática, validação local dos dígitos verificadores e consulta à API da [**CPFHub.io**](https://www.cpfhub.io/) entrega uma experiência fluida para o usuário e segurança real para o sistema. Com tempo de resposta de aproximadamente 900ms e suporte a mais de 13 linguagens no backend, a integração funciona em qualquer stack tecnológica.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e adicione validação de CPF ao seu formulário HTML hoje mesmo.

