HTTPS é o protocolo HTTP transportado por TLS. No Node.js, os módulos node:https e node:tls permitem criar servidores e clientes seguros, configurar certificados, validar identidades e inspecionar conexões criptografadas.
Em muitas arquiteturas, TLS termina no CDN, ingress ou load balancer. Mesmo assim, entender o funcionamento é importante para comunicação interna, mTLS, diagnóstico de certificados e configuração correta de clientes.
Criando um servidor HTTPS
import { createServer } from 'node:https';
import { readFileSync } from 'node:fs';
const server = createServer({
key: readFileSync('./certs/server-key.pem'),
cert: readFileSync('./certs/server-cert.pem'),
}, (req, res) => {
res.writeHead(200, {
'content-type': 'application/json; charset=utf-8',
});
res.end(JSON.stringify({ status: 'secure' }));
});
server.listen(8443);A chave privada deve ter permissões restritas. Não a inclua no repositório ou imagem pública.
Certificado, chave e cadeia
Os principais arquivos são:
- chave privada do servidor;
- certificado do servidor;
- certificados intermediários;
- CA raiz confiada pelo cliente.
O servidor precisa apresentar a cadeia intermediária correta. Um navegador pode funcionar por possuir intermediários em cache enquanto outro cliente falha.
Cliente HTTPS
import { get } from 'node:https';
get('https://example.com', (res) => {
console.log(res.statusCode);
res.resume();
}).on('error', (error) => {
console.error(error);
});Por padrão, o cliente valida a cadeia e o hostname. Não desative essa validação em produção.
CA privada
import { Agent } from 'node:https';
import { readFileSync } from 'node:fs';
const agent = new Agent({
ca: readFileSync('./certs/internal-ca.pem'),
keepAlive: true,
});Adicionar uma CA privada é melhor que usar rejectUnauthorized:false. Distribua a CA por secret manager ou configuração controlada.
NODE_EXTRA_CA_CERTS
O ambiente pode adicionar certificados confiáveis por uma variável de processo:
NODE_EXTRA_CA_CERTS=/etc/certs/internal-ca.pem node dist/server.jsTeste o comportamento na versão do Node.js utilizada e não substitua silenciosamente o armazenamento de confiança sem documentação.
mTLS
Mutual TLS autentica servidor e cliente por certificados.
const server = createServer({
key: readFileSync('./certs/server-key.pem'),
cert: readFileSync('./certs/server-cert.pem'),
ca: readFileSync('./certs/client-ca.pem'),
requestCert: true,
rejectUnauthorized: true,
}, (req, res) => {
const certificate = req.socket.getPeerCertificate();
res.end(JSON.stringify({ subject: certificate.subject }));
});Não use somente o Common Name como autorização. Valide SAN, emissor, uso de chave e identidade definida pela PKI.
Cliente mTLS
const agent = new Agent({
cert: readFileSync('./certs/client-cert.pem'),
key: readFileSync('./certs/client-key.pem'),
ca: readFileSync('./certs/server-ca.pem'),
keepAlive: true,
});Rotacione certificados e proteja a chave do cliente.
SNI
Server Name Indication permite servir certificados diferentes no mesmo IP. O Node.js pode selecionar um contexto seguro de acordo com o hostname.
import tls from 'node:tls';
const contexts = new Map([
['api.example.com', tls.createSecureContext({
key: readFileSync('./certs/api-key.pem'),
cert: readFileSync('./certs/api-cert.pem'),
})],
]);
const server = createServer({
SNICallback(servername, callback) {
callback(null, contexts.get(servername));
},
});Valide servername e sempre tenha comportamento para host desconhecido.
ALPN
ALPN negocia protocolos como HTTP/1.1 e HTTP/2 durante o handshake.
const socket = tls.connect({
host: 'example.com',
port: 443,
ALPNProtocols: ['h2', 'http/1.1'],
});
socket.on('secureConnect', () => {
console.log(socket.alpnProtocol);
});Versões mínimas
Evite protocolos antigos. Configure uma versão mínima compatível com sua política:
const server = createServer({
key,
cert,
minVersion: 'TLSv1.2',
});A política pode exigir TLS 1.3 ou permitir TLS 1.2 por compatibilidade. Teste clientes reais.
Ciphers
O Node.js e OpenSSL fornecem padrões seguros para versões modernas. Personalizar cifras sem conhecimento pode reduzir segurança ou compatibilidade. Siga política organizacional e scanners confiáveis.
Renegociação
Renegociação TLS possui riscos e limites. Não dependa dela para autenticação tardia. Prefira novas conexões ou protocolos desenhados para a necessidade.
Session resumption
Reutilizar sessões TLS pode reduzir custo de handshake. Keep-alive normalmente oferece ganho maior porque evita o handshake completo. Monitore a taxa de novas conexões antes de otimizar tickets e sessões.
Rotação de certificados
Certificados expiram e precisam ser renovados antes. Estratégias:
- terminar TLS em proxy que recarrega automaticamente;
- reiniciar gradualmente instâncias com novos secrets;
- atualizar contexto seguro em processo;
- monitorar data de expiração;
- testar cadeia e hostname após rotação.
Não espere o dia da expiração.
Recarregando contexto
Servidores podem atualizar material criptográfico conforme as APIs disponíveis. Planeje uma atualização atômica: carregue e valide os novos arquivos antes de substituir o contexto.
Certificados em containers
Monte certificados como secrets somente leitura. Evite copiá-los durante build. Confirme permissões, caminho e atualização do volume.
Terminação no proxy
Quando o proxy termina TLS e envia HTTP ao Node.js, proteja a rede interna e configure corretamente headers como X-Forwarded-Proto. Só confie nesses headers de proxies conhecidos.
TLS até o backend
Em ambientes de alto requisito, o proxy pode estabelecer HTTPS ou mTLS até cada backend. Isso protege tráfego interno, mas aumenta custo operacional de certificados e diagnóstico.
Erros comuns de certificado
- certificado expirado;
- hostname não presente no SAN;
- cadeia intermediária incompleta;
- CA privada ausente;
- relógio incorreto;
- certificado para uso inadequado;
- chave não corresponde ao certificado;
- permissão de arquivo incorreta.
Inspecionando o certificado remoto
const socket = tls.connect({
host: 'example.com',
port: 443,
servername: 'example.com',
});
socket.on('secureConnect', () => {
const cert = socket.getPeerCertificate(true);
console.log({
subject: cert.subject,
issuer: cert.issuer,
validFrom: cert.valid_from,
validTo: cert.valid_to,
});
socket.end();
});Não registre certificados completos em produção sem necessidade.
Hostname e IP
Conectar por IP enquanto o certificado pertence a um hostname causa falha de validação. Informe servername quando a conexão precisa usar SNI e validar o nome esperado.
Timeout de handshake
Clientes lentos podem manter handshakes abertos. Defina timeout apropriado no servidor e no proxy. Monitore falhas e duração do handshake.
Proteção da chave privada
Use permissões mínimas, secret manager, criptografia em repouso e rotação. Em ambientes mais críticos, considere HSM ou serviço de terminação que evite expor a chave ao processo.
Logs seguros
Registre código do erro, hostname conhecido, emissor e expiração quando necessário. Nunca registre chave privada, conteúdo de secrets ou credenciais de cliente.
Observabilidade
Meça:
- handshakes por segundo;
- duração do handshake;
- falhas de validação;
- versão TLS negociada;
- ALPN;
- certificados próximos da expiração;
- conexões reutilizadas;
- erros por dependência.
Testes
Teste certificado válido, expirado, hostname incorreto, CA ausente, mTLS sem cliente, rotação, HTTP/2, proxy e shutdown. Use certificados exclusivos de teste.
Erros comuns
- usar rejectUnauthorized false;
- guardar chave no Git;
- não enviar intermediários;
- confiar em qualquer X-Forwarded-Proto;
- não monitorar expiração;
- customizar cifras sem política;
- não testar mTLS na rotação;
- registrar material sensível;
- não alinhar keep-alive e timeout.
Fluxo recomendado
Prefira terminação gerenciada quando possível. Para TLS no Node.js, valide certificados, use CAs corretas, proteja chaves, configure versões modernas e automatize rotação. Combine com HTTP/2 no Node.js, HTTP Keep-Alive, Graceful Shutdown e Health Checks.
Consulte a documentação oficial de TLS e a documentação oficial de HTTPS no Node.js.



