Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

ETag no Node.js

Atualizado em: 2 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

ETag é um identificador de versão enviado no cabeçalho HTTP. Ele permite que clientes e caches perguntem se uma representação mudou. Quando o valor enviado em If-None-Match corresponde ao ETag atual, o servidor responde 304 Not Modified sem reenviar o corpo.

Em aplicações Node.js, ETags reduzem banda e serialização de respostas. Também podem ser usados para controle de concorrência em atualizações, evitando que um cliente sobrescreva uma versão mais recente.

Resposta com ETag

HTTP/1.1 200 OK
ETag: "product-42-v7"
Cache-Control: private, no-cache
Content-Type: application/json

O cliente armazena o corpo e o validador.

Revalidação

GET /products/42 HTTP/1.1
If-None-Match: "product-42-v7"

Se a representação continua igual:

HTTP/1.1 304 Not Modified
ETag: "product-42-v7"
Cache-Control: private, no-cache

A resposta 304 não deve incluir corpo.

Gerando ETag por versão

Quando o banco possui versão ou timestamp confiável:

function productEtag(product) {
  return `"product-${product.id}-v${product.version}"`;
}

app.get('/products/:id', async (req, res) => {
  const product = await repository.findById(req.params.id);
  const etag = productEtag(product);

  res.setHeader('etag', etag);
  res.setHeader('cache-control', 'private, no-cache');

  if (req.headers['if-none-match'] === etag) {
    res.status(304).end();
    return;
  }

  res.json(product);
});

O ETag precisa representar exatamente a resposta. Se campos, idioma ou permissões alteram o corpo, a versão deve refletir isso.

Gerando hash do conteúdo

import { createHash } from 'node:crypto';

function createEtag(body) {
  const hash = createHash('sha256')
    .update(body)
    .digest('base64url');

  return `"${hash}"`;
}

Calcular hash depois de serializar garante correspondência com o corpo, mas custa CPU e memória. Para respostas grandes, use versão armazenada ou hash pré-calculado.

Serialização determinística

Objetos semanticamente iguais podem gerar JSON diferente se a ordem de propriedades mudar. Um ETag por hash pode mudar sem alteração real. Use serialização estável ou versão de domínio.

Strong ETag

Um ETag forte indica que as representações são equivalentes byte a byte:

ETag: "abc123"

Weak ETag

Um ETag fraco indica equivalência semântica, mas não necessariamente byte a byte:

ETag: W/"abc123"

ETags fracos são úteis quando pequenas diferenças de serialização não importam para cache, mas não servem para todos os usos de Range e concorrência.

If-None-Match

Em GET e HEAD, If-None-Match permite retornar 304. O cabeçalho pode conter vários valores e o curinga:

If-None-Match: "v1", "v2"
If-None-Match: *

Não compare apenas a string completa quando precisa suportar a sintaxe geral. Use uma biblioteca ou parser adequado.

If-Match

If-Match permite atualização otimista. O servidor executa a operação somente se a versão ainda corresponde:

app.put('/products/:id', async (req, res) => {
  const current = await repository.findById(req.params.id);
  const currentEtag = productEtag(current);
  const ifMatch = req.headers['if-match'];

  if (!ifMatch) {
    res.status(428).json({ error: 'precondition_required' });
    return;
  }

  if (ifMatch !== currentEtag) {
    res.status(412).json({ error: 'precondition_failed' });
    return;
  }

  const updated = await repository.updateIfVersion(
    current.id,
    current.version,
    req.body,
  );

  res.setHeader('etag', productEtag(updated));
  res.json(updated);
});

A validação final deve acontecer atomicamente no banco. Verificar e atualizar em chamadas separadas pode sofrer corrida.

Status 412

412 Precondition Failed informa que If-Match ou outra precondição não foi atendida. O cliente precisa buscar a versão atual, resolver o conflito e tentar novamente.

Status 428

428 Precondition Required pode exigir If-Match para evitar lost updates. Documente o contrato da API.

Criação condicional

If-None-Match: * pode indicar que a operação só deve ocorrer se o recurso ainda não existir. Isso ajuda em criação idempotente, desde que o armazenamento aplique a condição atomicamente.

ETag e compressão

Gzip e Brotli produzem bytes diferentes. Estratégias:

  • ETag fraco baseado no conteúdo original;
  • ETag específico por variante;
  • proxy recalcula ou altera;
  • cache diferencia por Accept-Encoding.

Teste a infraestrutura final. Não presuma que o ETag do Node.js chega intacto ao cliente.

ETag e Vary

Se a resposta varia por idioma:

Vary: Accept-Language

O ETag precisa identificar a versão daquela variante. Um único ETag para português e inglês pode retornar 304 incorretamente.

Resposta personalizada

Não use um ETag público compartilhado se o corpo contém dados diferentes por usuário. Configure cache privado e gere ETag da representação autorizada.

ETag e CDN

CDNs podem revalidar a origem usando If-None-Match. Isso reduz transferência entre CDN e backend. Garanta que 304 inclua Cache-Control, ETag e Vary.

ETag e banco

Boas fontes de versão:

  • coluna inteira incrementada;
  • revision ID;
  • hash persistido;
  • timestamp com precisão suficiente;
  • versão de agregado.

Timestamp em segundos pode não distinguir duas alterações rápidas. Uma versão inteira é mais previsível.

ETag agregado

Uma lista depende de vários registros. Opções:

  • versão global da coleção;
  • maior updatedAt combinado com contagem;
  • hash de IDs e versões;
  • versão por página e filtros;
  • cache que armazena corpo e ETag juntos.

Evite consultar e serializar toda a coleção apenas para descobrir que não mudou.

Paginação

Cada URL paginada possui sua própria representação. Filtros, ordenação, cursor e campos selecionados fazem parte da chave e do ETag.

HEAD deve retornar o mesmo ETag que GET, sem corpo. Isso permite verificar metadados.

Range requests

ETags fortes podem ser usados com If-Range para downloads parciais. Se o recurso mudou, o servidor envia a representação completa em vez de combinar partes incompatíveis.

Last-Modified

É possível enviar ETag e Last-Modified. If-None-Match possui precedência nas condições relevantes. ETag oferece maior precisão, enquanto Last-Modified é simples para arquivos.

Middleware

Frameworks podem gerar ETags automaticamente. Entenda se são fortes ou fracos, se o hash é calculado sobre o corpo comprimido e qual o custo. Não duplique o header.

Custo de CPU

Hash de resposta grande em cada requisição pode eliminar o ganho. Meça com CPU profiling e cacheie o resultado junto com a versão.

Segurança

ETags persistentes podem ser usados como identificadores de rastreamento se forem únicos por usuário e mantidos por muito tempo. Não use dados pessoais ou segredos no valor.

Ataques de comparação

ETag não é mecanismo de autorização ou integridade criptográfica para o cliente. Ele é um validador HTTP. Continue autenticando e validando permissões.

Observabilidade

Meça:

  • respostas 200 e 304;
  • If-None-Match recebido;
  • If-Match recebido;
  • 412 e 428;
  • bytes economizados;
  • tempo para gerar ETag;
  • conflitos de versão;
  • revalidação por CDN.

Testes com curl

curl -i http://localhost:3000/products/42

curl -i \
  -H 'If-None-Match: "product-42-v7"' \
  http://localhost:3000/products/42

Teste também ETag errado, fraco, múltiplos valores, wildcard, compressão e idioma.

Concorrência

Para PUT:

curl -i -X PUT \
  -H 'If-Match: "product-42-v7"' \
  -H 'Content-Type: application/json' \
  --data '{"name":"Novo nome"}' \
  http://localhost:3000/products/42

Erros comuns

  • gerar ETag sem considerar variante;
  • retornar corpo em 304;
  • omitir Cache-Control e Vary em 304;
  • calcular hash caro em toda chamada;
  • usar timestamp com baixa precisão;
  • comparar If-Match sem atualização atômica;
  • usar ETag como autenticação;
  • não tratar múltiplos valores;
  • duplicar ETag do framework;
  • ignorar proxy e compressão.

Fluxo recomendado

Use uma versão de domínio quando possível, gere validadores por representação, retorne 304 para GET e aplique If-Match atomicamente em escritas. Combine com Cache HTTP no Node.js, Compressão HTTP, Rate Limiting e Circuit Breaker.

Consulte a referência de ETag e a especificação de semântica HTTP.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita