Cache HTTP no Nginx para WordPress: Guia Prático 2026

20/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

Resposta rápida: configuração mínima de cache HTTP no Nginx para WordPress

Para acelerar um WordPress no Nginx sem quebrar área logada nem WooCommerce, combine três camadas: (1) expires para assets estáticos no navegador, (2) fastcgi_cache no location que repassa PHP ao PHP-FPM, e (3) fastcgi_cache_bypass condicionado a cookies wordpress_logged_in_, comment_author_, wp-postpass_ e ao método POST. Exponha $upstream_cache_status via add_header X-Cache-Status e valide com curl -I. Esse é o caminho mais estável para sites que servem PHP-FPM diretamente — a maioria das instalações WordPress em VPS.

proxy_cache vs fastcgi_cache: qual escolher

A diferença é o módulo Nginx que entrega a resposta dinâmica. proxy_cache (do ngx_http_proxy_module) cacheia respostas vindas de um upstream HTTP — útil quando o Nginx atua como reverse proxy diante de Apache, Node, Varnish ou outro Nginx. fastcgi_cache (do ngx_http_fastcgi_module) cacheia respostas que o próprio Nginx busca no PHP-FPM via FastCGI, que é o caminho mais comum em WordPress.

Tabela: Tabela comparativa proxy_cache vs fastcgi_cache (módulo, quando usar, diretivas-chave, cenário WordPress típico)
Tabela comparativa proxy_cache vs fastcgi_cache (módulo, quando usar, diretivas-chave, cenário WordPress típico)
Critério proxy_cache fastcgi_cache
Módulo ngx_http_proxy_module ngx_http_fastcgi_module
Quando usar Nginx como reverse proxy diante de outro HTTP Nginx falando direto com PHP-FPM
Diretivas-chave proxy_cache_path, proxy_cache_key, proxy_cache_valid, proxy_cache_bypass fastcgi_cache_path, fastcgi_cache_key, fastcgi_cache_valid, fastcgi_cache_bypass
Cenário típico WordPress Nginx → Apache/LiteSpeed Nginx → PHP-FPM (stack LEMP padrão)

Regra prática: se o seu location ~ \.php$ usa fastcgi_pass, vá de fastcgi_cache. Se usa proxy_pass, vá de proxy_cache.

Headers do navegador: Cache-Control e Expires com a diretiva expires

Para CSS, JS, fontes e imagens, a diretiva expires do ngx_http_headers_module resolve em uma linha: ela emite automaticamente o header Expires e o Cache-Control: max-age=N equivalente. Segundo a documentação oficial do Nginx, expires max gera Expires apontando para 31 de dezembro de 2037 e Cache-Control: max-age=315360000 — efetivamente 10 anos.

Tabela: Tabela de TTLs recomendados por tipo de asset (HTML dinâmico, CSS/JS versionado, imagens, fontes) com valor de expires e Cache-Control suger
Tabela de TTLs recomendados por tipo de asset (HTML dinâmico, CSS/JS versionado, imagens, fontes) com valor de expires e Cache-Control suger
location ~* \.(css|js|woff2|svg|jpg|jpeg|png|webp|avif)$ {
    expires 30d;
    add_header Cache-Control "public";
    access_log off;
}

location ~* \.(ico|gif)$ {
    expires max;
}

Detalhe que muitos tutoriais ignoram: quando Cache-Control: max-age e Expires coexistem na resposta, a RFC 9111 (HTTP Caching) determina que max-age prevalece para clientes HTTP/1.1. Expires só sobra como fallback para clientes muito antigos. Para assets versionados (com hash na URL, gerados por build), o expires longo é seguro porque mudanças no arquivo geram nova URL. Em conjunto com otimizar imagens do WordPress, esse bloco resolve a maior parte do peso do front-end.

Configurando fastcgi_cache passo a passo

Fluxo de 5 passos: (1) confirme que o binário tem ngx_http_fastcgi_module com nginx -V 2>&1 | tr ' ' '\n' | grep fastcgi; (2) declare o fastcgi_cache_path no contexto http {}; (3) ative fastcgi_cache no location PHP; (4) adicione regras de bypass; (5) exponha X-Cache-Status e teste.

Tabela: Tabela de cookies/URIs que devem disparar fastcgi_cache_bypass no WordPress (wordpress_logged_in_, comment_author_, wp-postpass_, /wp-admin,
Tabela de cookies/URIs que devem disparar fastcgi_cache_bypass no WordPress (wordpress_logged_in_, comment_author_, wp-postpass_, /wp-admin,

Bloco completo no nginx.conf (contexto http):

fastcgi_cache_path /var/cache/nginx/wp levels=1:2 keys_zone=WORDPRESS:100m
                   inactive=60m max_size=1g use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;
fastcgi_cache_lock on;

No server {} do site:

set $skip_cache 0;

# Métodos não-GET nunca cacheiam
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "")   { set $skip_cache 1; }

# URIs administrativas e dinâmicas do WordPress/WooCommerce
if ($request_uri ~* "/wp-admin/|/wp-login.php|/xmlrpc.php|/cart|/checkout|/my-account|sitemap(_index)?\.xml") {
    set $skip_cache 1;
}

# Cookies de sessão: logados, comentaristas, posts protegidos, carrinho
if ($http_cookie ~* "wordpress_logged_in_|comment_author_|wp-postpass_|woocommerce_items_in_cart|wp_woocommerce_session_") {
    set $skip_cache 1;
}

location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid 200 301 302 60m;
    fastcgi_cache_valid 404 1m;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache    $skip_cache;

    add_header X-Cache-Status $upstream_cache_status always;
}

O keys_zone=WORDPRESS:100m reserva 100 MB de memória compartilhada para metadados (chega para centenas de milhares de chaves). O max_size=1g é o teto em disco. inactive=60m remove entradas não acessadas em 60 minutos, independente do TTL.

Bypass obrigatório: cookies de login, comentários, wp-postpass e POST

O guia oficial de content caching do NGINX recomenda explicitamente usar fastcgi_cache_bypass/proxy_cache_bypass baseados em cookies e argumentos para não cachear conteúdo de autenticados. Em WordPress, os cookies-gatilho são consistentes há anos:

Gráfico: max-age=315360000 — A diretiva `expires max` no Nginx gera Cache-Control: max-age=315360000 (≈10 anos) e Expires em 2037.
max-age=315360000 — A diretiva `expires max` no Nginx gera Cache-Control: max-age=315360000 (≈10 anos) e Expires em 2037.
  • wordpress_logged_in_* — usuário autenticado no wp-admin ou front-end.
  • comment_author_* — visitante que comentou (vê o nome pré-preenchido).
  • wp-postpass_* — post protegido por senha já desbloqueado.
  • woocommerce_items_in_cart / wp_woocommerce_session_* — carrinho ativo no WooCommerce.

Para evitar if em série, dá para usar map no contexto http:

map $http_cookie $wp_skip_cookie {
    default 0;
    "~*wordpress_logged_in_"        1;
    "~*comment_author_"             1;
    "~*wp-postpass_"                1;
    "~*woocommerce_items_in_cart"   1;
    "~*wp_woocommerce_session_"     1;
}

map $request_method $wp_skip_method {
    default 0;
    POST    1;
}

E no server: set $skip_cache "${wp_skip_cookie}${wp_skip_method}"; — qualquer valor diferente de 00 ativa o bypass. Esse mesmo princípio aparece no guia de cache HTTP no Nginx para WordPress sem quebrar login, que complementa cenários adicionais de área logada.

Validando HIT/MISS com curl -I e X-Cache-Status

A variável $upstream_cache_status, definida no ngx_http_upstream_module, expõe os estados MISS, HIT, BYPASS, EXPIRED, STALE, UPDATING e REVALIDATED. Com o add_header X-Cache-Status ativo, a validação é direta:

Gráfico: 7 estados — $upstream_cache_status expõe sete estados possíveis: MISS, BYPASS, EXPIRED, STALE, UPDATING, REVALIDATED e HIT.
7 estados — $upstream_cache_status expõe sete estados possíveis: MISS, BYPASS, EXPIRED, STALE, UPDATING, REVALIDATED e HIT.
$ curl -I https://seusite.com/blog/post-exemplo/
HTTP/2 200
server: nginx
content-type: text/html; charset=UTF-8
x-cache-status: MISS

$ curl -I https://seusite.com/blog/post-exemplo/
HTTP/2 200
x-cache-status: HIT

Se a segunda requisição ainda vier MISS, alguma regra de bypass está disparando — geralmente um cookie de sessão deixado pelo navegador. Teste de uma janela anônima, ou direto via curl sem -b. Para confirmar bypass de logado: curl -I -H 'Cookie: wordpress_logged_in_abc=1' ... deve retornar X-Cache-Status: BYPASS.

Invalidação: TTL, ngx_cache_purge e estratégias sem purge nativo

O Nginx open source não tem purge nativo. As três rotas práticas:

Gráfico: RFC 9111 — A RFC 9111 (publicada em junho de 2022) é a especificação atual de HTTP Caching e substitui a RFC 7234.
RFC 9111 — A RFC 9111 (publicada em junho de 2022) é a especificação atual de HTTP Caching e substitui a RFC 7234.
  1. TTL curto + stale-while-revalidate: fastcgi_cache_valid 200 10m; com fastcgi_cache_use_stale updating entrega conteúdo levemente velho enquanto revalida. Aceitável para blogs.
  2. Módulo ngx_cache_purge (FRiCKLE): adiciona a diretiva fastcgi_cache_purge, acionada por um location interno restrito. Requer compilar Nginx com o módulo ou usar pacote que já o inclua.
  3. Nginx Plus: oferece API de purge oficial — caminho corporativo.

Exemplo de purge com ngx_cache_purge:

location ~ /purge(/.*) {
    allow 127.0.0.1;
    deny all;
    fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}

Esse endpoint pode ser chamado por um plugin WordPress no hook save_post ou por script disparado pós-deploy. Para automatizar pelo terminal, WP-CLI para automação permite encadear o purge a comandos de publicação. Atenção: o repositório do ngx_cache_purge tem manutenção intermitente — valide compatibilidade com sua versão do Nginx antes de adotar como única estratégia.

Composição com CDN (Cloudflare/BunnyCDN) e plugins WordPress

Cache no Nginx e CDN de borda atuam em camadas diferentes e somam. Conforme a documentação da Cloudflare sobre Cache-Control, o edge respeita os headers de origem para definir TTL — ou seja, o que o seu Nginx emite no Cache-Control dos assets estáticos é o que a Cloudflare/BunnyCDN vai honrar. Recomendações de coexistência:

  • Assets estáticos: TTL longo no Nginx (expires 30d ou max) e deixe o CDN espelhar.
  • HTML: prefira cachear apenas no Nginx (fastcgi_cache) e deixar o CDN em modo bypass cookie para o HTML, evitando servir página de logado a anônimo.
  • Plugins (WP Rocket, W3 Total Cache, LiteSpeed Cache): se você já tem fastcgi_cache bem configurado, plugins de page cache se tornam redundantes — mantenha-os só para minificação e lazy-load. Rodar dois page caches gera incoerência de invalidação.

A escolha entre Cloudflare ou BunnyCDN para WordPress impacta como você dispara purge no CDN após o purge local.

Erros comuns que quebram WordPress com cache no Nginx

  • Cachear wp-admin ou wp-login.php: usuário entra com nonce de outra sessão, dashboard quebra. Sempre inclua essas URIs no skip_cache.
  • Cachear AJAX (admin-ajax.php, wp-json/): nonces ficam congelados e endpoints REST retornam dados de outro usuário. Bypass obrigatório.
  • Não bypassar POST: formulários de contato e checkout passam a retornar a página de confirmação de outro visitante.
  • Cachear com cookie de sessão: o WooCommerce gera wp_woocommerce_session_* em qualquer visita ao carrinho — sem bypass, o cache serve carrinho alheio.
  • Esquecer fastcgi_no_cache: fastcgi_cache_bypass sozinho lê do cache mas grava; sem fastcgi_no_cache, respostas de logados poluem o cache.
  • TTL maior que a frequência de publicação: sem purge, post novo demora a aparecer. Ajuste fastcgi_cache_valid ao seu ritmo editorial.
  • add_header sem always: o header X-Cache-Status some em respostas 4xx/5xx, dificultando debug. Use add_header ... always;.

FAQ

Posso usar fastcgi_cache junto com WP Rocket?

Tecnicamente sim, mas é redundante. Se o Nginx já cacheia HTML, desative o page cache do WP Rocket e mantenha apenas otimizações de assets (minify, lazy-load, defer).

Quanto tempo de TTL é seguro para HTML?

Entre 10 minutos e 1 hora para sites de conteúdo. Para e-commerce, prefira TTL curto (1–5 min) ou bypass completo de páginas com preço.

O cache do Nginx funciona com HTTPS?

Sim. O TLS termina no Nginx; o cache opera no nível da resposta HTTP, sem relação com a camada de transporte.

Preciso reiniciar o Nginx para limpar o cache?

Não. Basta apagar o diretório do fastcgi_cache_path (rm -rf /var/cache/nginx/wp/*) e o Nginx recria sob demanda. Reload (nginx -s reload) não é necessário só para invalidar.

Deixe um comentário