Full page cache com Varnish no Magento 2: setup que realmente dá hit
Colocar o Varnish na frente do Magento 2 é fácil. Fazer ele servir a maioria das requisições do cache é outro trabalho. Muitas lojas rodam Varnish com um hit rate tão baixo que quase todo visitante ainda espera pelo PHP. Este artigo cobre como o full page cache do Magento funciona com o Varnish na 2.4.x, como configurar a partir do VCL gerado, como medir o hit rate e os motivos de sempre para ele ficar baixo.
Como funciona o full page cache do Magento
O Magento decide quais páginas são cacheáveis. O Varnish as guarda e as serve. Os dois conversam por headers HTTP:
- O
Cache-Controlna resposta diz se a página pode ser cacheada e por quanto tempo. Páginas que não podem ser cacheadas são enviadas com valores private ou no-store. - O
X-Magento-Tagslista as cache tags de tudo que está na página, comocat_p_123para um produto oucat_c_45para uma categoria. O Varnish guarda o header junto com o objeto e o remove antes de enviar a resposta ao navegador. - O cookie
X-Magento-Varyguarda um hash do contexto que muda a página: grupo do cliente, moeda, store e valores parecidos. Ele faz parte da chave de cache, então um cliente de atacado logado não recebe a página do varejo.
Quando uma entidade muda, o Magento envia uma requisição PURGE ao Varnish com um header X-Magento-Tags-Pattern. O VCL transforma isso num ban em todos os objetos cujas tags casam. É assim que o save de um produto invalida a página do produto, as categorias dele e qualquer página que o liste, sem limpar o resto.
O conteúdo específico do cliente, como o minicart, a mensagem de boas-vindas e o contador da wishlist, nunca faz parte do HTML cacheado. Ele é carregado por JavaScript a partir de customer/section/load e guardado no navegador. Esse é o mecanismo de private content, e é a razão de um full page cache conseguir funcionar numa loja com login e carrinho.
Configurando o Varnish a partir do VCL gerado
Não escreva um VCL do zero e não copie um de um blog antigo. Gere a partir da sua versão do Magento, porque o VCL do core muda entre releases.
bin/magento config:set system/full_page_cache/caching_application 2
bin/magento config:set system/full_page_cache/ttl 86400
bin/magento varnish:vcl:generate \
--export-version=6 \
--backend-host=127.0.0.1 \
--backend-port=8080 \
--access-list=127.0.0.1 \
--output-file=/etc/varnish/default.vcl
bin/magento setup:config:set --http-cache-hosts=127.0.0.1:6081
bin/magento cache:flush
caching_applicationigual a 2 troca o full page cache do cache interno da aplicação para o Varnish.--export-versionprecisa bater com a versão major do Varnish que você roda. Veja nos requisitos de sistema quais versões a sua release do Magento suporta.--access-listlista os hosts autorizados a enviarPURGE. Deixe só os seus servidores de aplicação.--http-cache-hostsnoapp/etc/env.phpdiz ao Magento para onde enviar as requisições de purge. Com vários nós de Varnish, liste todos separados por vírgula.
O que conferir no arquivo gerado
- A probe do health check aponta para
/pub/health_check.php. Se o document root do seu web server já épub/, como deveria ser, troque a URL da probe para/health_check.php. Senão o Varnish marca o backend como doente. - Se o TLS termina na frente do Varnish, garanta que o terminador envie
X-Forwarded-Proto. O VCL gerado usa isso no hash para que as versões HTTP e HTTPS não se misturem. - Páginas de categoria grandes produzem headers
X-Magento-Tagslongos. Se passarem dos limites do Varnish, o fetch no backend falha com 503. A documentação da Adobe recomenda aumentar os parâmetroshttp_resp_hdr_lenehttp_resp_sizepor esse motivo.
Mantenha suas próprias alterações numa seção pequena e comentada e regenere o arquivo base quando atualizar o Magento. Um diff entre o arquivo gerado antigo e o novo mostra o que o core mudou.
Medindo o hit rate
Você não conserta o que não mede. O Varnish dá os números direto:
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss -f MAIN.cache_hitpass -f MAIN.s_pass
varnishncsa -F '%U%q %{Varnish:hitmiss}x %{Varnish:handling}x'
O primeiro comando dá os totais desde que o Varnish iniciou. O segundo transmite cada requisição indicando se foi hit, miss ou pass. Deixe rodando alguns minutos com tráfego normal e ordene os misses e passes por URL. Os padrões aparecem rápido: a mesma URL com query strings diferentes, um tipo de página inteiro que sempre dá pass, ou requisições POST que você não esperava.
Em developer mode o Magento adiciona um header X-Magento-Cache-Debug com HIT ou MISS, o que ajuda no staging. Em produção use o varnishlog para uma URL só:
varnishlog -g request -q 'ReqURL ~ "^/women/tops.html"'
Olhe as linhas VCL_call e VCL_return para ver se a requisição foi para hash, pass ou pipe, e os headers da resposta do backend para ver o que o Magento enviou.
Por que o hit rate fica baixo
cacheable="false" no layout
Essa é a armadilha clássica. Um único block marcado com cacheable="false" num arquivo de layout torna a página inteira não cacheável, não só aquele block. Se ele está no handle default, a storefront inteira fica não cacheável.
<referenceContainer name="sidebar.additional">
<block class="Acme\Promo\Block\Countdown" name="acme.promo.countdown"
template="Acme_Promo::countdown.phtml" cacheable="false"/>
</referenceContainer>
Autores de extensões adicionam isso quando um block mostra dados dinâmicos ou do cliente, porque deixa o block correto nos testes. Ache todas as ocorrências:
grep -rn 'cacheable="false"' app/code app/design vendor --include=*.xml | grep -v "/adminhtml/"
Páginas como carrinho, checkout e conta do cliente são feitas para não serem cacheáveis. Qualquer coisa em default, catalog_category_view, catalog_product_view ou cms_index_index merece um olhar mais atento. A correção é renderizar a parte dinâmica por private content ou por uma chamada AJAX e remover a flag.
Query strings que fragmentam o cache
Cada URL única é um objeto de cache separado. Links de marketing adicionam parâmetros como utm_source ou gclid, e sem tratamento cada clique de campanha é um miss. O VCL gerado já remove uma lista de parâmetros comuns de marketing antes de fazer o hash. Confira essa lista contra os parâmetros que o seu tráfego realmente usa e amplie se precisar. Parâmetros que mudam a página, como filtros de layered navigation, ordenação e paginação, precisam ficar.
Contextos demais
Tudo que entra no X-Magento-Vary multiplica o número de versões de cada página. Muitos grupos de clientes, várias moedas e valores de contexto customizados adicionados por extensões via Magento\Framework\App\Http\Context podem espalhar o tráfego por tantas variantes que poucas chegam a ficar quentes. Revise o que é adicionado ao contexto HTTP e mantenha só o que realmente muda o HTML.
Cookies e sessões em páginas cacheáveis
Se um módulo customizado define um cookie ou grava na sessão enquanto renderiza uma página cacheável, ou ele quebra o cache ou, pior, vaza o estado de um visitante para o HTML cacheado. Dados de sessão não têm lugar num block que renderiza numa página em cache.
Purges amplos demais
Um purge total frequente esvazia o cache toda hora. As causas comuns são um cron job ou importação que chama cache:flush ou limpa o tipo full_page, uma sincronização com o ERP que salva todo produto a cada execução mesmo sem mudança, e atualizações de estoque de produtos exibidos em toda página. Registre as requisições de purge no Varnish por um dia e veja o que as dispara.
varnishlog -g request -q 'ReqMethod eq "PURGE"' -i ReqHeader
Private content do jeito certo
Quando um block precisa mostrar algo específico do cliente numa página em cache, use customer data sections em vez de desligar o cache. Uma section é uma classe PHP que implementa Magento\Customer\CustomerData\SectionSourceInterface e retorna um array. Ela é registrada no di.xml e lida em JavaScript pelo módulo Magento_Customer/js/customer-data no Luma, ou pelo evento equivalente no Hyvä.
Para atualizar uma section depois de uma ação, declare no etc/frontend/sections.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
<action name="acme_promo/coupon/apply">
<section name="cart"/>
<section name="acme-promo"/>
</action>
</config>
Depois de um POST para essa action, o navegador recarrega essas sections. O HTML continua em cache e compartilhado, e só uma pequena resposta JSON é pessoal.
Hole punching com ESI
Alguns conteúdos são iguais para todos, mas mudam com mais frequência que a página ao redor. Para isso o Magento suporta Edge Side Includes. Um block com atributo ttl no layout é renderizado como um include ESI quando o Varnish é a cache application, e o Varnish o busca e o cacheia separadamente, com sua própria duração.
<block class="Acme\Promo\Block\Banner" name="acme.promo.banner"
template="Acme_Promo::banner.phtml" ttl="600"/>
Use com moderação. Cada block ESI é outra requisição ao backend quando expira, e o ESI é a ferramenta errada para conteúdo específico do cliente, que pertence ao private content.
Grace e warm up
O período de grace deixa o Varnish servir um objeto um pouco velho enquanto busca um novo em segundo plano, e mantém o site no ar se o backend ficar doente por pouco tempo. O VCL gerado suporta isso e a opção --grace-period define o valor. Depois de um deploy ou de um flush total o cache está vazio, então os primeiros visitantes pagam pela renderização sem cache. Um warm up simples que requisita a home, as principais categorias e os produtos mais vendidos a partir do sitemap reduz esse impacto. Mantenha lento o bastante para não sobrecarregar o backend que você acabou de publicar.
FAQ
Um block com cacheable=false desabilita o cache da página inteira?
Sim. Se qualquer block no layout da página está marcado com cacheable=false, o Magento envia a página inteira como não cacheável. Coloque no handle default e nenhuma página da storefront fica em cache.
Como vejo se uma página do Magento foi hit no Varnish?
Em developer mode o Magento adiciona um header X-Magento-Cache-Debug com HIT ou MISS. Em produção use o varnishlog para a URL ou o varnishncsa com o campo Varnish:hitmiss para ver como cada requisição foi tratada.
Como o Magento faz purge no Varnish quando um produto muda?
O Magento envia uma requisição PURGE com um header X-Magento-Tags-Pattern listando as cache tags afetadas. O VCL gerado transforma isso num ban em todos os objetos em cache que carregam essas tags no X-Magento-Tags.
Posso cachear páginas para clientes logados?
Sim. Clientes logados recebem páginas em cache por contexto, principalmente grupo do cliente, e as partes pessoais, como carrinho e nome, vêm de customer data sections carregadas por JavaScript.
Atendo como freelancer em Magento e Adobe Commerce, de desenvolvimento de módulos e auditorias de performance a migrações e projetos em Hyvä. Se isso fizer sentido para você, fale comigo.
Artigos relacionados
Cache e performance do GraphQL no Magento 2: guia prático
Deixe o GraphQL do Magento 2 rápido: queries GET, cache no Varnish, X-Magento-Cache-Id, identities do @cache em resolvers customizados, batching de N+1 e limites de complexidade.
Customer data sections no Magento 2: como funciona o conteúdo privado
Como as customer data sections mantêm páginas em cache personalizadas: sections.xml, section sources, customer-data.js, invalidação e os erros que quebram o FPC.
Magento 2: tipos de cache, clean, flush e cache type próprio
O que cada cache type do Magento 2 guarda, a diferença real entre cache:clean e cache:flush, caches invalidados e como criar seu próprio cache type.
Core Web Vitals no Magento 2: como corrigir LCP, INP e CLS
Onde lojas Magento 2 com Luma perdem nos Core Web Vitals e como corrigir LCP, INP e CLS: resposta do servidor, CSS, fontes, imagens, galeria e scripts de terceiros.