IPv6 para Desenvolvedores Web
O que desenvolvedores web precisam saber sobre IPv6: URLs com colchetes, armazenamento em banco de dados, programação de sockets e bugs comuns a evitar.
Se você tem ignorado IPv6 porque "será um problema para depois", depois é agora. Redes móveis executam IPv6-first. Alguns usuários atrás de NAT64 só podem alcançá-lo via IPv6. Até localhost na sua máquina de desenvolvimento provavelmente resolve para ::1 antes de 127.0.0.1.
Este guia cobre o que você realmente precisa saber: lidar com IPv6 em URLs, armazenar endereços em bancos de dados, programação de sockets e os bugs que vão te morder se você não for cuidadoso.
TL;DR - Resumo rápido
Pontos-chave:
- Use colchetes em URLs:
http://[2001:db8::1]:8080/ - Armazene endereços como strings ou tipos nativos de IP, não como inteiros
- Bind em
::para aceitar IPv4 e IPv6 em uma única socket - Teste com conectividade apenas IPv6, não apenas dual-stack
- Regex de validação de endereços devem suportar formatos IPv4 e IPv6
Ir para: URLs | Armazenamento em Banco de Dados | Programação de Sockets | Bugs Comuns
IPv6 em URLs#
Endereços IPv6 contêm dois-pontos, o que conflita com o separador de porta em URLs. A solução é notação de colchetes:
http://[2001:db8::1]:8080/api/users
https://[2606:2800:220:1:248:1893:25c8:1946]/Sem colchetes, parsers não conseguem dizer onde o endereço termina e a porta começa. Isso se aplica a:
- URLs HTTP/HTTPS
- Conexões WebSocket (
ws://[::1]:3000) - Strings de conexão de banco de dados
- Qualquer esquema URI
Ao gerar URLs programaticamente, envolva endereços IPv6 em colchetes. Ao analisar, remova-os antes de validação ou armazenamento.
Armazenamento em Banco de Dados#
O erro clássico é usar VARCHAR(15) para endereços IP. Isso cabe IPv4 (máximo 15 caracteres: 255.255.255.255) mas não IPv6.
PostgreSQL tem um tipo nativo INET que lida com IPv4 e IPv6:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
ip_address INET NOT NULL
);
-- Validação e normalização automática
INSERT INTO users (ip_address) VALUES ('2001:db8::1');
INSERT INTO users (ip_address) VALUES ('192.0.2.1');O tipo INET armazena endereços eficientemente, valida-os na inserção e suporta operações de rede como correspondência de sub-rede com operadores << e >>.
MySQL não tem tipo nativo, então use:
VARBINARY(16)- armazena a representação binária (16 bytes para IPv6, 4 para IPv4)VARCHAR(45)- armazena a representação string (comprimento máximo de IPv6 expandido)
Armazenamento binário é mais eficiente mas requer funções de conversão:
-- Armazenar
INSERT INTO users (ip_address) VALUES (INET6_ATON('2001:db8::1'));
-- Recuperar
SELECT INET6_NTOA(ip_address) FROM users;Normalização importa. Endereços IPv6 têm múltiplas representações válidas:
2001:0db8:0000:0000:0000:0000:0000:00012001:db8::1(comprimido)2001:db8:0:0:0:0:0:1(parcialmente comprimido)
Sempre normalize antes de comparar ou indexar. A maioria das bibliotecas tem uma função de forma canônica.
Programação de Sockets#
A API de socket trata IPv4 e IPv6 como famílias de endereço separadas. Aplicativos modernos devem suportar ambos.
Node.js usa sockets dual-stack por padrão:
const http = require('http');
// Vincula a :: (todos os endereços IPv6) e aceita IPv4 via mapeamento
const server = http.createServer((req, res) => {
res.end(`Seu IP: ${req.socket.remoteAddress}`);
});
server.listen(3000, '::');O endereço :: é o equivalente IPv6 de 0.0.0.0, aceitando conexões em todas as interfaces. A maioria dos sistemas suporta endereços IPv6 mapeados para IPv4 (::ffff:192.0.2.1), permitindo que um único socket IPv6 lide com ambos os protocolos.
Python requer tratamento dual-stack explícito:
import socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(('::', 8080))
sock.listen(5)A flag IPV6_V6ONLY controla se o socket aceita apenas IPv6 ou ambos IPv4 e IPv6.
Go torna dual-stack o padrão:
listener, err := net.Listen("tcp", ":8080")
// Escuta automaticamente em IPv4 e IPv6Para conexões cliente, sempre use getaddrinfo() (ou equivalente da sua linguagem) em vez de resolver endereços manualmente. Lida com IPv4, IPv6 e cenários dual-stack corretamente:
import socket
# Não faça isso
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
# Faça isso - funciona para IPv4 e IPv6
addr_info = socket.getaddrinfo('example.com', 80, socket.AF_UNSPEC, socket.SOCK_STREAM)
sock = socket.socket(addr_info[0][0], addr_info[0][1])
sock.connect(addr_info[0][4])Testes Locais#
A maioria dos sistemas resolve localhost para ambos ::1 (IPv6) e 127.0.0.1 (IPv4). Verifique seu /etc/hosts:
127.0.0.1 localhost
::1 localhostSe seu aplicativo vincula apenas a 127.0.0.1, não aceitará conexões IPv6. Vincule a :: para dual-stack ou vincule explicitamente a ambos os endereços.
Para testar comportamento apenas IPv6, desabilite IPv4 na sua interface de loopback ou use [::1] explicitamente no seu cliente HTTP:
curl http://[::1]:3000/Para testes realistas, use uma rede de teste apenas IPv6. Muitos provedores de nuvem oferecem instâncias apenas IPv6 que forçam você a lidar com casos extremos como NAT64/DNS64.
CDN e Servidores de Origem#
A maioria das CDNs (Cloudflare, Fastly, AWS CloudFront) suportam IPv6 por padrão. Elas servem conteúdo sobre IPv6 para clientes e traduzem para IPv4 para sua origem se necessário.
O problema: se você expõe APIs diretamente (contornando a CDN), seu servidor de origem deve suportar IPv6. Verifique se seu DNS tem registros AAAA e seu firewall permite tráfego IPv6.
Bugs Comuns#
Validação regex. Padrões regex IPv6 são notoriamente complexos e geralmente errados:
// Não faça isso
const ipv6Regex = /^([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$/i;
// Falha em compressão ::, endereços IPv4-mapeados, IDs de zona...Use uma biblioteca de parsing adequada. Toda linguagem principal tem uma que lida com IPv6 corretamente. Para validação, use nossa ferramenta Validador IPv6.
Endereços IPv4 hardcoded. Busque em seu código por padrões como:
const API_SERVER = 'http://192.168.1.100:3000'; // Não funcionará com IPv6Use hostnames em vez disso, ou suporte ambas as famílias de endereço.
Limitação de taxa baseada em IP. Endereços IPv6 mudam frequentemente devido a extensões de privacidade. Limitação de taxa por endereço exato falhará. Limite por sub-rede /64 em vez disso:
// Ruim
const key = `rate:${ipAddress}`;
// Melhor para IPv6
const key = `rate:${ipv6ToSubnet64(ipAddress)}`;Bibliotecas sem suporte IPv6. Clientes HTTP mais antigos, drivers de banco de dados e bibliotecas de rede podem não lidar com IPv6. Teste com endereços IPv6 reais, não apenas localhost. Se uma biblioteca falhar, verifique por atualizações ou alternativas.
Casos extremos de parsing de URL. Garanta que seu router/framework lida com endereços IPv6 entre colchetes:
GET http://[2001:db8::1]:8080/api/users
Host: [2001:db8::1]:8080Alguns parsers incorretamente incluem os colchetes no cabeçalho Host ou falham em extrair o endereço corretamente.
Lista de Verificação#
Seu aplicativo está pronto para IPv6 quando:
- Colunas de banco de dados podem armazenar strings de 45 caracteres ou usar tipos IP nativos
- Servidores vinculam a
::ou explicitamente escutam em IPv4 e IPv6 - Código cliente usa
getaddrinfo()ou equivalente para resolução de nomes - Validação de IP não depende de regex
- Sem endereços IPv4 hardcoded em configuração
- Limitação de taxa e geolocalização lidam com sub-redes IPv6
- Parsing de URL lida com notação entre colchetes
IPv6 não é mais opcional. Construa desde o início, e você evitará a migração dolorosa depois.
Artigos Relacionados#
- Fundamentos do IPv6 — Entenda formatos de endereços e conceitos básicos para desenvolvimento
- Tipos de Endereço IPv6 — Aprenda os diferentes tipos de endereços que você precisa tratar no código
Valide Endereços
Use o Validador IPv6 e a ferramenta Ping para testar seu código com endereços IPv6 reais.