Cache HTTP no Nginx para WordPress sem quebrar login

15/05/2026
Foto do Perfil do Autor developer
ESCRITO POR developer

Andre Almeida. Especialista em Inteligencia Artificial, programador a mais de 22 Anos. C.E.O & Fundador da Fabricando Sua Ideia

O que exatamente deve ser cacheado no WordPress e o que deve ficar fora do cache?

Em WordPress, cache HTTP bom é o que reduz download de arquivos repetidos sem encostar em conteúdo que muda por usuário, sessão ou ação de login. Na prática, você cacheia com agressividade CSS, JS, imagens, fontes e, em alguns casos, HTML público que muda pouco; deixa fora do cache páginas logadas, /wp-admin, /wp-login.php, carrinho, checkout, área de membros, manutenção e qualquer resposta que dependa de cookie de autenticação.

Se você tratar tudo como estático, o problema aparece rápido: um usuário autenticado pode receber página de visitante, um admin pode ver uma versão antiga do painel, ou uma tela de manutenção pode “grudar” no cache. Em site WordPress de produção, a regra é simples: o que é compartilhado por todos pode ser cacheado; o que depende de identidade, permissão ou estado da sessão deve ser exceção.

Também vale separar a camada de cache HTTP da camada de cache de aplicação. Um plugin de performance pode servir páginas e assets já otimizados, mas isso não elimina a necessidade de headers corretos no Nginx. O ideal é fazer o Nginx falar de forma consistente com browsers, proxies e CDNs, sem tentar transformar resposta dinâmica em arquivo estático.

Como separar cache de estáticos, HTML dinâmico e áreas sensíveis

A organização mais segura é criar blocos location diferentes por tipo de resposta. Assets estáticos recebem cache longo; HTML público recebe cache curto ou desativado; áreas sensíveis recebem bypass explícito. Em vez de depender de uma regra única “para o site inteiro”, você deixa a intenção clara no arquivo de configuração.

Uma divisão prática costuma ficar assim: um bloco para extensões estáticas, outro para rotas de administração e login, outro para HTML público do front-end. O critério operacional é direto: se a URL termina em um asset versionado, ela pode receber TTL alto; se a resposta depende de cookie, login ou permissão, a regra muda para bypass ou no-store. Esse desenho reduz erro humano porque cada classe de recurso tem política própria. Em ambiente real, isso facilita manutenção e evita que uma regra global de header acabe afetando rotas críticas.

Exemplo de estrutura mental:

  • estáticos: arquivos versionados, com expiração longa;
  • HTML público: curto, revalidável ou sem cache;
  • áreas sensíveis: no-store ou bypass total;
  • conteúdo autenticado: sempre fora do cache padrão.

Se o seu site usa plugin de manutenção, página de login customizada ou área de membros, trate cada uma dessas rotas como exceção explícita. O ganho aqui não é só desempenho; é previsibilidade operacional.

Cache-Control, Expires e ETag: qual manda na política?

Os três headers não fazem a mesma coisa. Cache-Control é o header moderno para definir diretivas de cache em requisições e respostas. Expires define uma data e hora de expiração da resposta para cache HTTP. Já ETag não diz por quanto tempo guardar; ele ajuda na revalidação, para o cliente ou intermediário confirmar se o recurso mudou antes de baixar tudo de novo.

Na prática, o cabeçalho que deve mandar na política é o Cache-Control. A documentação do MDN deixa claro que, quando a resposta traz Cache-Control com max-age ou s-maxage, o Expires é ignorado. Por isso, não faz sentido montar uma política moderna confiando apenas em Expires e depois achar que ele vai prevalecer sobre Cache-Control.

Uma leitura útil é esta:

Header Função Quando usar
Cache-Control Diretiva moderna de cache Quase sempre, como política principal
Expires Data/hora de expiração Compatibilidade e cenários legados
ETag Validação de versão do recurso Quando vale revalidar sem baixar o corpo

Se você ainda encontrar recomendações com Pragma: no-cache, trate como herança de HTTP/1.0. Segundo a documentação da MDN, Pragma é compatibilidade legado e não substitui Cache-Control em HTTP/1.1. Em site moderno, ele pode aparecer por compatibilidade, mas não deve ser o centro da estratégia.

Qual configuração prática de Nginx usar para arquivos estáticos?

Para estáticos, a meta é deixar o navegador e caches intermediários reutilizarem o arquivo por bastante tempo, desde que ele seja versionado. Em WordPress, isso costuma funcionar bem para .css, .js, imagens, fontes e ícones. Se seus assets mudam com deploy e têm hash no nome, cache longo faz sentido; se não têm versionamento, reduza a agressividade.

location ~* \\.(?:css|js|mjs|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000, immutable";
    access_log off;
    log_not_found off;
}

Esse bloco faz duas coisas importantes: o expires 30d define uma janela de validade e o Cache-Control reforça a política moderna. Se o arquivo for realmente versionado, o immutable ajuda o browser a não revalidar algo que não vai mudar. Em produção, isso reduz requisições desnecessárias e alivia tanto o Nginx quanto a origem.

Se você usa CDN na frente, o benefício aparece em cascata: o browser reutiliza, o proxy reutiliza e o servidor de origem vê menos tráfego. Em alguns cenários, o header Age ajuda a entender há quanto tempo um objeto ficou em cache no proxy intermediário.

Qual configuração prática de Nginx usar para HTML dinâmico e páginas não cacheáveis?

HTML dinâmico exige outra postura. Aqui, o padrão mais seguro é desativar cache ou usar um TTL muito curto, porque o conteúdo muda com login, geolocalização, carrinho, idioma, cookie ou personalização. Em WordPress, isso vale especialmente para home com conteúdo rotativo, páginas de busca interna e rotas com comportamento por sessão.

location / {
    try_files $uri $uri/ /index.php?$args;
    add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
    add_header Expires "0" always;
}

Esse modelo é conservador: impede que a resposta fique cacheada por engano e funciona bem quando o conteúdo não deve ser reutilizado entre usuários. Em vez de tentar adivinhar quais respostas dinâmicas são “seguras”, você começa pelo padrão mais restritivo e libera cache apenas onde há previsibilidade.

Em alguns sites, vale usar um TTL curto em páginas públicas, por exemplo 60 segundos, mas isso só faz sentido se você souber que o conteúdo não muda a cada clique. Em WordPress de produção, a economia real costuma vir mais dos estáticos e da validação condicional do que de cachear HTML sem critério.

Como impedir cache em wp-admin, login e páginas de usuários autenticados?

Esse é o ponto mais sensível. Em WordPress, páginas logadas, administração e manutenção devem ser tratadas como exceções e não como conteúdo cacheável padrão. Isso vale para /wp-admin/, /wp-login.php, telas de preview, áreas protegidas e qualquer resposta que dependa de cookie de autenticação.

location ^~ /wp-admin/ {
    add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
    add_header Expires "0" always;
    try_files $uri $uri/ /index.php?$args;
}

location = /wp-login.php {
    add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
    add_header Expires "0" always;
    try_files $uri =404;
}

Em ambiente real, eu também evito deixar regras globais de cache sem filtro de cookie. Se o site tem login, área de membros ou checkout, a política precisa considerar que resposta autenticada não deve atravessar a mesma rota de cache do visitante anônimo. O erro mais caro em produção é servir conteúdo privado como se fosse público.

Se houver página de manutenção, mantenha-a fora do cache padrão também. O ecossistema WordPress mostra claramente que manutenção e disponibilidade temporária são estados especiais; a mesma lógica vale para qualquer tela de administração ou operação.

Como o fluxo de validação com If-None-Match e 304 reduz transferências desnecessárias?

Quando você não quer baixar o corpo inteiro de novo, entra a revalidação condicional. O ETag identifica a versão do recurso, o If-None-Match manda essa versão de volta ao servidor, e o servidor responde com 304 Not Modified se nada mudou. Nesse caso, o cliente reaproveita a cópia em cache sem transferir o conteúdo outra vez.

O fluxo prático é este:

  1. o cliente recebe uma resposta com ETag: "abc123";
  2. na próxima requisição, envia If-None-Match: "abc123";
  3. o servidor compara o valor recebido com a versão atual;
  4. se o conteúdo for o mesmo, responde 304 Not Modified;
  5. o navegador usa a cópia local e economiza banda e tempo.

Isso é especialmente útil em HTML e assets que mudam pouco, porque você mantém validação sem retransmitir o corpo completo. A documentação da MDN descreve exatamente esse papel do ETag e do If-None-Match no ciclo de validação condicional. Em rede móvel ou em conexões mais lentas, a diferença de experiência é perceptível.

O ponto operacional importante: 304 não significa “novo conteúdo” nem “erro”; significa que a versão local continua válida. Se você monitora a aplicação, esse status costuma ser um sinal de que a revalidação está funcionando e não de falha.

Micro-check rápido: na primeira resposta, espere 200 OK com ETag e, em algumas camadas, Age: 0; na segunda, envie If-None-Match com o valor recebido e confirme 304 Not Modified sem body. Se isso não acontecer, o ciclo de revalidação ainda não fechou.

Como testar se a configuração está correta com headers como Age, Cache-Control e 304?

Depois de aplicar a configuração, teste com curl -I e com o navegador em aba anônima e autenticada. Você quer confirmar três coisas: o tipo certo de cache por rota, o comportamento de revalidação e a ausência de vazamento entre usuário logado e visitante.

Checklist prático:

  • Cache-Control: deve refletir a política que você definiu;
  • Expires: deve aparecer coerente com o TTL, mas não contradizer Cache-Control;
  • ETag: deve existir quando você quer revalidação condicional;
  • Age: em camadas com proxy ou CDN, deve subir conforme o objeto envelhece no cache;
  • 304: deve aparecer quando a revalidação confirmar que o recurso não mudou.

Exemplo de verificação:

curl -I https://exemplo.com/wp-content/uploads/2026/05/logo.png
curl -I https://exemplo.com/
curl -I https://exemplo.com/wp-admin/

Para os estáticos, espere um Cache-Control agressivo e, se houver cache intermediário, pode aparecer Age. Para HTML dinâmico, espere headers mais restritivos. Para wp-admin e login, o objetivo é não permitir cache compartilhado.

Se você tiver CDN na frente, rode o teste tanto na borda quanto diretamente na origem quando possível. Às vezes a origem está correta, mas a camada intermediária altera o resultado esperado. Em discussão da comunidade NGINX, por exemplo, a ausência ou diferença do header Age já foi associada a confusão no comportamento de caches downstream.

Quais erros comuns quebram cache ou geram risco de conteúdo sensível vazando?

O erro mais comum é misturar política de estático com HTML dinâmico. Outro erro é usar uma regra global de add_header e assumir que ela vale para tudo, sem revisar herança de headers e exceções por location. Em versões recentes do NGINX, a herança de headers ganhou nuance adicional, então vale revisar a sintaxe e testar de novo após upgrades.

Outros problemas frequentes:

  • cachear páginas autenticadas porque o bloco location não excluiu cookie ou rota sensível;
  • usar Expires como se fosse superior ao Cache-Control;
  • manter Pragma como se fosse política moderna de cache;
  • aplicar TTL longo em HTML sem versionamento;
  • esquecer de validar /wp-admin, /wp-login.php e páginas de manutenção;
  • não testar a resposta com e sem autenticação.

Se você já usa plugin de performance, não desligue a configuração do Nginx por reflexo. O ideal é alinhar as duas camadas: plugin para otimização de aplicação e Nginx para cabeçalhos e regras HTTP consistentes. Quando os dois lados estão desencontrados, o sintoma típico é comportamento inconsistente entre navegador, proxy e origem.

Na prática, a melhor configuração é a que você consegue explicar em uma revisão rápida do nginx.conf: estáticos com cache longo, HTML público com política curta ou controlada, rotas sensíveis fora do cache, e validação condicional quando ela realmente reduz custo. Se essa leitura não fica clara em cinco minutos, a chance de erro em produção é alta.

Para complementar a estratégia de performance e controle de comportamento do site, vale também ver IndexNow no WordPress: como configurar em 2026, especialmente se você quer alinhar cache, atualização de conteúdo e descoberta por mecanismos de busca.

Depois de publicar a mudança, revise uma página pública, uma página logada e um asset estático em janela anônima e em sessão autenticada. Esse teste de cobertura costuma pegar erro de configuração que parece correto no arquivo, mas falha na resposta real.

Categorias Sem categoria

Deixe um comentário