O módulo node:http2 permite criar clientes e servidores HTTP/2 diretamente no Node.js. O protocolo mantém uma conexão multiplexada, comprime cabeçalhos e possibilita que várias requisições compartilhem a mesma sessão sem depender de diversas conexões TCP paralelas.
HTTP/2 pode reduzir latência em aplicações com muitos recursos ou chamadas concorrentes, mas não corrige automaticamente gargalos no banco, no código ou em serviços externos. A adoção precisa considerar TLS, proxies, balanceadores, limites de streams e compatibilidade com HTTP/1.1.
HTTP/2 e HTTP/1.1
No HTTP/1.1, cada conexão processa uma sequência limitada de requisições e pode sofrer bloqueio entre respostas. HTTP/2 divide a conexão em streams independentes. Cada stream possui identificador próprio e pode transportar cabeçalhos e dados simultaneamente.
Os principais conceitos são:
- sessão: a conexão HTTP/2 ativa;
- stream: uma requisição ou resposta lógica;
- frames: unidades transmitidas pelo protocolo;
- multiplexação: várias streams na mesma sessão;
- HPACK: compressão de cabeçalhos;
- flow control: controle de quantidade de dados em trânsito.
Criando um servidor seguro
import { createSecureServer } from 'node:http2';
import { readFileSync } from 'node:fs';
const server = createSecureServer({
key: readFileSync('./certs/server-key.pem'),
cert: readFileSync('./certs/server-cert.pem'),
allowHTTP1: true,
});
server.on('stream', (stream, headers) => {
const path = headers[':path'];
if (path === '/health') {
stream.respond({
':status': 200,
'content-type': 'application/json; charset=utf-8',
});
stream.end(JSON.stringify({ status: 'ok' }));
return;
}
stream.respond({ ':status': 404 });
stream.end();
});
server.listen(8443);allowHTTP1 permite negociar HTTP/1.1 quando o cliente não suporta HTTP/2. A negociação normalmente ocorre por ALPN durante o handshake TLS.
Cabeçalhos pseudo
HTTP/2 usa pseudo-cabeçalhos iniciados por dois-pontos, como:
:method;:path;:scheme;:authority;:status.
Não trate esses campos como cabeçalhos comuns enviados arbitrariamente pelo usuário. Valide método, caminho e autoridade antes de construir regras de roteamento.
API compatível com HTTP/1
O servidor também pode usar a API de requisição e resposta familiar:
server.on('request', (req, res) => {
res.writeHead(200, {
'content-type': 'text/plain; charset=utf-8',
});
res.end('Resposta HTTP/2');
});A API de compatibilidade facilita frameworks, mas recursos específicos do protocolo podem exigir acesso a stream e sessão.
Criando um cliente
import { connect } from 'node:http2';
const client = connect('https://localhost:8443', {
rejectUnauthorized: false,
});
client.on('error', console.error);
const request = client.request({
':method': 'GET',
':path': '/health',
});
let body = '';
request.setEncoding('utf8');
request.on('data', (chunk) => body += chunk);
request.on('end', () => {
console.log(body);
client.close();
});
request.end();rejectUnauthorized:false só serve para certificado local de desenvolvimento. Em produção, valide a cadeia de confiança e o hostname.
Reutilizando sessões
A principal vantagem do cliente aparece quando a mesma sessão atende várias chamadas. Não crie uma conexão para cada requisição.
class Http2Client {
constructor(origin) {
this.origin = origin;
this.session = null;
}
getSession() {
if (!this.session || this.session.closed || this.session.destroyed) {
this.session = connect(this.origin);
this.session.on('error', (error) => {
logger.warn({ error }, 'Sessão HTTP/2 falhou');
});
}
return this.session;
}
}Uma implementação de produção precisa controlar concorrência, reconnect com backoff, timeout, fechamento e múltiplas origens.
Limite de streams concorrentes
O servidor remoto informa quantas streams simultâneas aceita. Abrir streams ilimitadas aumenta filas e memória. Mantenha um limite local e observe as configurações recebidas.
client.on('remoteSettings', (settings) => {
logger.info({
maxConcurrentStreams: settings.maxConcurrentStreams,
}, 'Configurações HTTP/2 recebidas');
});Se a demanda excede a capacidade da sessão, use fila limitada ou mais sessões de forma controlada.
Flow control
Cada stream e sessão possuem janelas de controle de fluxo. Quando o consumidor lê lentamente, o emissor precisa reduzir o ritmo. Respeite backpressure e não acumule respostas completas em memória sem necessidade.
stream.on('data', (chunk) => {
processChunk(chunk);
});Para arquivos e grandes respostas, conecte streams com pipeline.
Timeouts
Defina prazos para sessão e streams:
request.setTimeout(5000, () => {
request.close();
});Um timeout deve cancelar o trabalho associado. Não basta rejeitar uma Promise enquanto a stream continua consumindo recursos.
GOAWAY
O frame GOAWAY informa que a sessão não aceitará novas streams. O cliente deve parar de enviar chamadas nela, permitir conclusão das streams válidas e abrir uma nova sessão quando necessário.
client.on('goaway', (errorCode, lastStreamID) => {
logger.info({ errorCode, lastStreamID }, 'GOAWAY recebido');
});Retries precisam considerar idempotência. Uma operação de escrita não deve ser repetida cegamente.
Server push
O protocolo oferece server push, mas navegadores e infraestruturas modernas podem não aproveitar esse recurso. Prefira cache, preload e distribuição adequada antes de criar lógica complexa de push.
Proxy reverso
É comum terminar HTTP/2 no CDN, ingress ou load balancer e encaminhar HTTP/1.1 para o Node.js. Essa arquitetura ainda oferece benefícios ao cliente externo. Execute HTTP/2 no processo apenas quando houver necessidade de ponta a ponta ou comunicação interna específica.
gRPC
gRPC usa HTTP/2 como transporte. Para APIs gRPC, use bibliotecas próprias que tratam framing, metadados, streaming e status. O módulo http2 é baixo nível e não implementa o protocolo de aplicação.
Segurança
Defina limites para:
- tamanho de cabeçalhos;
- streams simultâneas;
- tempo de sessão;
- tamanho de corpo;
- velocidade mínima de envio;
- quantidade de sessões por cliente.
Mantenha Node.js, OpenSSL e proxies atualizados. Protocolos multiplexados exigem proteção contra clientes que abrem muitas streams e enviam dados lentamente.
Observabilidade
Monitore:
- sessões abertas;
- streams ativas;
- streams rejeitadas;
- GOAWAY e resets;
- latência por operação;
- bytes enviados e recebidos;
- erros por código;
- tempo de handshake TLS;
- event loop utilization.
Normalize rotas antes de usar labels de métricas. IDs e caminhos completos criam alta cardinalidade.
Graceful shutdown
No encerramento, pare de aceitar novas sessões, envie GOAWAY quando apropriado, aguarde streams em andamento e imponha prazo máximo. Depois destrua sessões restantes.
Testes
Teste HTTP/2 e fallback HTTP/1.1, múltiplas streams, cliente lento, GOAWAY, reset, certificado inválido, timeout, payload grande e shutdown. Use ferramentas que realmente negociem HTTP/2.
Erros comuns
- criar sessão por requisição;
- ignorar limite de streams;
- desativar validação TLS em produção;
- acumular corpos grandes;
- não tratar GOAWAY;
- repetir operações não idempotentes;
- ativar HTTP/2 sem verificar o proxy;
- não limitar recursos por cliente;
- achar que o protocolo resolve gargalos da aplicação.
Fluxo recomendado
Comece verificando se o proxy já oferece HTTP/2. Quando o Node.js precisar falar o protocolo diretamente, reutilize sessões, limite streams, aplique timeouts e respeite backpressure. Combine com Streams e Backpressure, Graceful Shutdown, Health Checks e testes com Autocannon.
Consulte a documentação oficial de HTTP/2 no Node.js e a especificação HTTP/2.



