Last-Modified é um cabeçalho de resposta HTTP que indica a data e hora da última modificação de um recurso no servidor, permitindo que o cliente faça requisições condicionais via If-Modified-Since. Se o recurso não mudou desde a data especificada, o servidor retorna 304 Not Modified sem enviar o corpo da resposta, economizando significativamente largura de banda. De acordo com RFC 7232 (IETF, 2014), requisições condicionais com Last-Modified reduzem o tempo de carregamento de páginas em 30-60% em visitas repetidas. O cabeçalho é automaticamente suportado pela maioria dos servidores HTTP e proxies.
Principais Pontos
Last-Modified é um cabeçalho HTTP que pertence ao grupo de cabeçalhos de requisições condicionais. O servidor o adiciona a uma resposta GET ou HEAD, indicando a data e hora da última modificação do recurso solicitado no formato HTTP-date: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. O cliente (navegador, aplicativo móvel, proxy) armazena esta data junto com o recurso em cache. Em uma requisição repetida, o cliente envia o cabeçalho If-Modified-Since com a mesma data, e o servidor a compara com o momento atual de modificação do recurso.
O protocolo de requisições condicionais com Last-Modified é definido na RFC 7232 e suportado por todos os servidores HTTP modernos. O formato da data é estritamente regulamentado — apenas GMT (Greenwich Mean Time) sem indicação de fuso horário. O servidor deve retornar a data em três formatos possíveis: RFC 1123 (padrão), RFC 850 (obsoleto) ou ANSI C asctime. Na prática, quase todos os servidores usam o formato RFC 1123 com comprimento fixo de 29 caracteres.
Last-Modified pertence à categoria de mecanismos de validação de cache: ele não diz ao cliente se a resposta pode ser armazenada em cache, mas fornece uma ferramenta para verificar a atualidade de um recurso já em cache. A política de cache é definida separadamente pelo cabeçalho Cache-Control. De acordo com um estudo da Akamai (2025), a configuração adequada de Last-Modified junto com Cache-Control reduz a carga nos servidores de origem em até 70% para conteúdo estático.
O cabeçalho Last-Modified foi definido ainda no HTTP/1.0 (RFC 1945, 1996) e se tornou um dos primeiros mecanismos de gerenciamento de cache na web. Antes do surgimento do ETag no HTTP/1.1, era a única maneira de fazer requisições condicionais. Apesar da idade, o cabeçalho permanece relevante devido à sua simplicidade — o servidor não precisa calcular um hash do conteúdo, basta ler o timestamp do arquivo do sistema de arquivos ou o campo updated_at do banco de dados.
O ciclo completo consiste em três etapas. Na primeira requisição, o servidor retorna o recurso com o cabeçalho Last-Modified e status HTTP 200 OK. O cliente armazena em cache a resposta junto com a data. Em uma requisição repetida, o cliente envia o cabeçalho If-Modified-Since com a data salva. O servidor compara esta data com o momento atual de modificação do recurso. Se o recurso não mudou — retorna 304 Not Modified com corpo vazio. Se mudou — 200 OK com novos dados e um novo Last-Modified.
// Primeira requisição — servidor retorna o recurso com uma data
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT
[{"id": 1, "name": "Alice"}]
// Requisição repetida — cliente envia a data salva
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT
// Resposta — dados não mudaram
HTTP/1.1 304 Not Modified
Para aplicativos móveis, o Last-Modified é especialmente útil para sincronização de dados. O aplicativo armazena a data da última atualização bem-sucedida e a envia ao servidor no If-Modified-Since. Se houver mais dados ou eles tiverem mudado — o servidor retorna o conjunto completo. Se não — 304, e o aplicativo usa a cópia local. OkHttp e URLSession suportam este mecanismo automaticamente através de sistemas de cache integrados.
Para arquivos estáticos, Nginx e Apache obtêm a data dos atributos do sistema de arquivos — mtime (hora de modificação). Para conteúdo dinâmico, o código do servidor deve definir explicitamente o Last-Modified com base na lógica de negócios: o campo updated_at do banco de dados, a data do último commit no Git, o timestamp do artefato de build. Se Last-Modified não for definido explicitamente, o servidor pode não enviar o cabeçalho, e o cliente não poderá fazer requisições condicionais por data.
Last-Modified e ETag realizam uma tarefa similar — permitir que o cliente verifique a atualidade do cache — mas têm diferenças fundamentais. Last-Modified usa um timestamp, ETag usa um identificador único de versão. Cada abordagem tem seus cenários onde é mais eficaz, e a especificação HTTP recomenda usar ambos os cabeçalhos juntos.
| Critério | Last-Modified | ETag |
|---|---|---|
| Essência | Data da última modificação | Identificador único de versão |
| Precisão | Até um segundo | Até um bit (hash) |
| Complexidade de implementação | Baixa — automática do sistema de arquivos | Média — requer cálculo de hash |
| Servidores em cluster | Problema: mtime pode diferir entre nós | Estável com dados idênticos entre nós |
| Suporte a intervalos | Não afeta requisições Range | Requer ETag forte para intervalos |
| Recomendação | Para estáticos e APIs simples | Para APIs onde precisão é importante |
A principal vantagem do Last-Modified é a simplicidade. O servidor não precisa calcular um hash do conteúdo, economizando recursos de CPU em cada requisição. Para projetos de alto tráfego servindo arquivos estáticos ou dados com timestamps claros, Last-Modified continua sendo a escolha ótima. ETag, por outro lado, fornece precisão absoluta — alterar uma única letra em uma resposta JSON mudará o ETag, mas pode não mudar a data (se o arquivo foi sobrescrito com a mesma versão).
A especificação recomenda retornar ambos os cabeçalhos simultaneamente. O servidor inclui tanto Last-Modified quanto ETag na resposta 200 OK. O cliente envia ambos os cabeçalhos condicionais — If-Modified-Since e If-None-Match. O servidor verifica primeiro o ETag (tem prioridade), depois o Last-Modified. Se pelo menos um sinalizar uma mudança — a resposta completa é retornada. Isso oferece máxima flexibilidade: ETag garante precisão, Last-Modified fornece uma verificação de fallback para clientes que não suportam ETag.
A configuração do Last-Modified depende do tipo de servidor. Para Nginx e Apache, Last-Modified é definido automaticamente para arquivos estáticos com base no mtime. Para aplicações dinâmicas, o cabeçalho deve ser definido no código do servidor. Vamos ver a configuração em plataformas populares.
// Express.js — definindo Last-Modified
app.get("/api/users", async (req, res) => {
const updatedAt = await getLastUpdate()
const ifModifiedSince = req.get("If-Modified-Since")
// Verificando If-Modified-Since
if (ifModifiedSince && new Date(ifModifiedSince)
>= updatedAt) {
return res.status(304).end()
}
const users = await getUsers()
res.set("Last-Modified", updatedAt.toUTCString())
res.json(users)
})
No exemplo Express.js, o servidor obtém a data da última atualização dos dados do banco, verifica If-Modified-Since do cliente e, se o cache ainda estiver atual, retorna 304. Se os dados mudaram — define um novo Last-Modified e retorna a resposta completa. toUTCString() converte a data para o formato HTTP exigido. Em produção, é bom armazenar updatedAt em cache no Redis para evitar consultar o banco a cada requisição.
Nginx define automaticamente Last-Modified para arquivos estáticos com base na hora da última modificação do arquivo. É possível desabilitar ou alterar este comportamento usando a diretiva etag (desabilitando ETag) ou através do módulo ngx_http_headers_module. Para requisições proxy ao backend, Last-Modified é passado da resposta upstream sem alterações. Importante: se o backend não retornar Last-Modified, o Nginx não o adicionará automaticamente para respostas dinâmicas.
Last-Modified tem várias limitações conhecidas. A principal é a precisão de segundo. Se um recurso mudar duas vezes dentro de um segundo, o cliente pode perder a nova versão. Na prática isso é um cenário raro, mas para atualizações de alta frequência (feeds de cotações, chats) o ETag é recomendado. A segunda limitação é o problema de clusterização: em servidores diferentes, um arquivo pode ter mtime diferente devido a cópia ou deploy, tornando o Last-Modified inconsistente.
A terceira limitação — o manuseio de If-Modified-Since com precisão de segundo pode levar a requisições desnecessárias ao consultar o servidor com frequência. Se o cliente envia If-Modified-Since a cada 500 ms, o servidor retorna 200 OK toda vez porque a data não mudou, mas o recurso já foi atualizado. A solução é usar uma combinação com ETag: ETag detectará a mudança dentro de um segundo, enquanto Last-Modified permanece como fallback.
O quarto problema — Last-Modified não distingue entre diferentes versões do mesmo recurso com a mesma data. Se um arquivo for restaurado de um backup e seu mtime coincidir com o original, o cliente não notará que o conteúdo mudou. ETag resolve este problema: o hash do conteúdo mudará garantidamente com qualquer alteração nos dados, independentemente do timestamp. Para dados críticos, sempre use ambos os cabeçalhos.
Perguntas Frequentes
Apenas GMT (Greenwich Mean Time) no formato RFC 1123: dia da semana, dia, mês, ano, horas:minutos:segundos. Exemplo: Wed, 02 Jul 2025 14:30:00 GMT. O fuso horário é sempre GMT, outros formatos não são permitidos.
Tecnicamente sim, mas viola a RFC 7232. Se o servidor retornar uma data futura, os clientes não atualizarão o recurso até que essa data chegue. Tal configuração é considerada um erro — a data deve estar no passado ou presente.
Não, requisições condicionais If-Modified-Since funcionam apenas com GET e HEAD. Requisições POST não são armazenadas em cache e não usam validação por data. Para verificações de atualidade em POST, use ETag ou mecanismos personalizados.
Cache-Control define a política de cache (tempo máximo de armazenamento, quem pode cachear), enquanto Last-Modified é um mecanismo de validação para cache expirado. Após o término do max-age, o cliente envia If-Modified-Since para verificar a atualidade.
Verifique se o servidor está realmente definindo o cabeçalho a partir da fonte correta — banco de dados, sistema de arquivos ou API. Para respostas dinâmicas, certifique-se de chamar explicitamente res.setHeader(“Last-Modified”, ...) no código do manipulador.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também