O WooCommerce de origem trava antes de o catálogo ficar interessante. Uma instalação limpa com algumas centenas de produtos parece rápida em staging, e o mesmo tema com dezenas de milhares de produtos e encomendas passa a ter um TTFB longo, os cart fragments fazem fila no arquivo da loja e a tabela wp_options cresce sem ninguém dar por isso.
O problema não é a plataforma, é a configuração e os anos de plugins acumulados. Uma loja WooCommerce rápida resulta de trabalho precisamente nestes pontos.
Este é um guia de terreno para preparar o WooCommerce para carga real. Pressupõe acesso à shell, WP-CLI e disponibilidade para ler o slow query log.
Como funciona a migração do WooCommerce para HPOS
Durante uma década, o WooCommerce guardou as encomendas como posts do tipo shop_order em wp_posts (a mesma tabela dos seus artigos de blog), com cada linha, morada e campo de metadados espalhado em linhas de wp_postmeta. Uma consulta como “encomendas de maio” obrigava a varrer uma tabela misturada com revisões, gravações automáticas e rascunhos, e depois a fazer três ou quatro JOIN com postmeta para reconstruir cada encomenda.
O High Performance Order Storage move as encomendas para wc_orders, wc_order_addresses, wc_order_operational_data e wc_orders_meta. As listas de encomendas consultam diretamente colunas indexadas, em vez de reconstruir cada linha a partir de postmeta.
O HPOS é o modo predefinido nas instalações novas desde o WooCommerce 8.2, lançado em outubro de 2023, segundo a documentação do HPOS. As lojas que arrancaram antes dessa versão continuam muitas vezes no armazenamento em posts, e é por isso que a migração ainda é o primeiro ponto da maioria das auditorias.
O que o HPOS não resolve sozinho:
- Plugins próprios que continuam a escrever em chaves de postmeta em vez da API de encomendas deixam de estar sincronizados, sem aviso, quando o modo de compatibilidade é desligado
- Plugins que leem encomendas com
get_post_meta($order_id, ...)em vez de$order->get_meta()passam a devolver valores vazios quando a tabela antiga deixa de ser a fonte de verdade - Relatórios construídos sobre
WP_Querycomshop_ordertêm de ser reescritos
Fluxo de trabalho:
- WooCommerce -> Definições -> Avançado -> Funcionalidades, ative o High Performance Order Storage com o modo de compatibilidade ligado
- Deixe a sincronização correr fora das horas de ponta; numa loja grande pode demorar várias horas
- Verifique com
wp wc hpos verify_data - Durante uma semana, teste todos os plugins que mexem em encomendas, com o modo de compatibilidade ainda a escrever em postmeta
- Desligue o modo de compatibilidade só depois de confirmar que cada um desses plugins lê do HPOS
wp wc hpos status
wp wc hpos sync --batch-size=500
wp wc hpos verify_dataComandos WP-CLI do HPOS desde o WooCommerce 8.9
Os tutoriais mais antigos usam o espaço de nomes wp wc cot. O WooCommerce 8.9.0 marcou-o como obsoleto: os comandos antigos continuam a funcionar, mas cada um imprime um aviso com o substituto em wp wc hpos, e verify_cot_data passou a chamar-se verify_data (CLIRunner.php). A documentação das ferramentas CLI do HPOS e os docblocks dos comandos indicam o conjunto atual:
wp wc hpos statusmostra as definições do HPOS e o estado da sincronizaçãowp wc hpos count_unmigratedconta as encomendas que ainda aguardam sincronizaçãowp wc hpos syncmigra encomendas em lotes, e--batch-sizevale 500 por predefiniçãowp wc hpos verify_datacompara os dois armazenamentos; a opção--re-migratevolta a sincronizar as encomendas com diferenças e pode sobrescrever uma encomenda, por isso experimente-a primeiro em stagingwp wc hpos diff 123mostra as diferenças de uma única encomendawp wc hpos compatibility-mode enableedisableligam e desligam a sincronização sem abrir o painelwp wc hpos cleanupremove os dados antigos em posts das encomendas migradas. A documentação classifica-o como destrutivo e, sem argumentos, não faz nada;wp wc hpos cleanup allprocessa todas as encomendas
Execute o cleanup apenas quando o modo de compatibilidade estiver desligado há tempo suficiente para que nenhum plugin leia a cópia antiga, e sempre depois de uma cópia de segurança da base de dados.
Como tornar um plugin próprio compatível com o HPOS
O WooCommerce avisa o dono da loja quando este tenta ativar o HPOS com um plugin ativo que se declarou incompatível, e mostra o mesmo aviso no ecrã de plugins. Nos plugins que não são extensões do WooCommerce, o aviso só aparece se o cabeçalho do plugin tiver a linha WC tested up to. Um plugin próprio declara o seu estado no ficheiro principal, como mostra o livro de receitas do HPOS:
add_action( 'before_woocommerce_init', function() {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
} );A declaração é uma promessa, não uma correção. O código por trás tem de carregar encomendas com wc_get_order() em vez de get_post() e escrever metadados através do objeto da encomenda (update_meta_data(), add_meta_data(), delete_meta_data() e depois save()) em vez de update_post_meta(). Assim é o WooCommerce que decide que tabelas recebem a escrita. O livro de receitas avisa que save() é uma operação relativamente cara, pelo que um ciclo que grava a encomenda após cada alteração de metadados é, ele próprio, um problema de performance. O código que precise de distinguir o motor de armazenamento pode chamar OrderUtil::custom_orders_table_usage_is_enabled().
Como desativar os cart fragments no WooCommerce
Uma loja WooCommerce mais antiga, ou uma com widget de carrinho no cabeçalho, mostra no GTmetrix ou no WebPageTest o pedido ?wc-ajax=get_refreshed_fragments em todas as páginas. É o WooCommerce a perguntar ao PHP se o ícone do carrinho no cabeçalho mudou: na página inicial, no blog, nos arquivos de categoria, na página “Sobre nós”. Mesmo com o carrinho vazio.
A regra de alcance mais simples:
add_action( 'wp_enqueue_scripts', function() {
if ( is_cart() || is_checkout() || is_product() ) {
return;
}
wp_dequeue_script( 'wc-cart-fragments' );
}, 11 );Plugins como o Perfmatters e o Asset CleanUp oferecem isto como um interruptor no painel. Os dois caminhos levam ao mesmo objetivo: os cart fragments só devem correr onde um ícone de carrinho precisa de se atualizar em direto.
Se precisar mesmo de um contador do carrinho no cabeçalho em todas as páginas, guarde-o em localStorage e atualize-o por JavaScript nos eventos de adicionar ao carrinho, em vez de consultar o servidor.
Quando o WooCommerce ainda carrega os cart fragments desde a versão 7.8
O WooCommerce 7.8 mudou o comportamento predefinido. Segundo o artigo do blog de programadores do WooCommerce Best practices for the use of the “cart fragments” API (16 de junho de 2023), o script deixou de ser incluído em todas as rotas da loja. Continua a carregar em três casos: nas páginas onde o widget do carrinho é apresentado, quando um script de terceiros regista o script de cart fragments como dependência e quando o código o inclui de forma explícita. O mesmo artigo nota que alguns temas, entre eles o Storefront, trazem o widget fixo nos seus templates.
Numa loja atual, o pedido na cascata é portanto um sintoma com uma causa que se encontra. Procure o handle wc-cart-fragments no código-fonte da página e descubra que template do tema ou que plugin o carrega. Retirá-lo sem essa verificação pode partir um carrinho de cabeçalho que o tema ainda atualiza através dos fragments, e um plugin que declara o script como dependência volta a incluí-lo de qualquer forma.
Object cache ou transients na base de dados
O WooCommerce usa transients de forma intensiva: intervalos de preços de variações, pesquisas de termos de atributos, cálculos de portes de envio. Sem object cache, os transients ficam em wp_options. Se são carregados em cada pedido depende da expiração: set_transient() guarda um transient sem expiração com autoload e um com expiração sem autoload, mais uma linha _transient_timeout_ separada. O WooCommerce guarda os transients de preços de variações e de variações filhas com 30 dias de expiração (armazenamento de dados de produtos variáveis), por isso sobretudo aumentam o número de linhas de wp_options. O peso do autoload vem das opções que os plugins escrevem sem expiração.
Escolha um backend de cache e mantenha-o. Ou Redis ou Memcached como object cache verdadeiro, ou transients na base de dados. Misturar os dois gera erros de invalidação difíceis de diagnosticar.
# Tamanho do autoload em wp_options
wp db query "SELECT SUM(LENGTH(option_value)) AS bytes, COUNT(*) AS rows FROM wp_options WHERE autoload IN ('yes', 'on', 'auto-on', 'auto');"
# As maiores opções com autoload
wp db query "SELECT option_name, LENGTH(option_value) AS bytes FROM wp_options WHERE autoload IN ('yes', 'on', 'auto-on', 'auto') ORDER BY bytes DESC LIMIT 20;"O WordPress executa muitas consultas para montar uma única página de produto: variações, preços, estado de stock. O Redis guarda os resultados dessas consultas na RAM. Com o Redis configurado no servidor, seja como serviço gerido do alojamento ou como instância própria de redis-server, e com o object-cache.php no lugar, set_transient() escreve no object cache em vez de em wp_options, os transients passam para a RAM e a pressão sobre o autoload desce.
Valores de autoload no WordPress 6.6 e o limite de 800 KB
O WordPress 6.6 mudou a forma de guardar o autoload. A nota de programadores do núcleo Options API: disabling autoload for large options introduz os valores on, off, auto, auto-on e auto-off; on e off são escolhas explícitas, enquanto auto-on e auto-off são definidos pelo próprio WordPress. A função wp_autoload_values_to_autoload() devolve yes, on, auto-on e auto, e é por isso que as consultas de auditoria acima filtram por essa lista. Uma consulta que só verifica autoload = 'yes' ignora todas as opções guardadas depois da atualização e mostra a loja mais limpa do que está.
A mudança trouxe dois limites. Uma opção com mais de 150 000 bytes não é carregada automaticamente, a não ser que o código o peça de forma explícita, e o filtro wp_max_autoloaded_option_size desloca esse limite. A Saúde do site assinala a instalação quando o tamanho total das opções com autoload passa de 800 KB. Essa verificação é o primeiro teste mais barato numa loja desconhecida, porque não exige acesso à shell e identifica o problema antes de alguém abrir um cliente de base de dados.
O que nunca deve ir para a cache numa loja WooCommerce
A cache estática ajuda os arquivos, as páginas de produto e os artigos do blog. Estraga tudo o que depende da sessão do utilizador, porque cada cliente tem o seu carrinho.
Exceções obrigatórias para qualquer cache de página ou regra da Cloudflare:
/carrinho/ou/cart//finalizar-compra/ou/checkout//minha-conta/ou/my-account/- Qualquer URL com
?wc-ajax= - Qualquer pedido com o cookie
woocommerce_items_in_cart - Os endpoints REST em
/wp-json/wc/
Faça cache do conteúdo estático (imagens, CSS, JS) no edge. O Cloudflare APO para WordPress consegue também guardar em cache o HTML para utilizadores sem sessão iniciada e respeita os cookies de sessão do WooCommerce se estiver bem configurado, mas confirme com curl -I no carrinho e no checkout que recebe cf-cache-status: BYPASS. Uma cache mal configurada que mostra o carrinho de outro cliente é um incidente de RGPD, não um ganho de performance.
Consultas de produtos variáveis e o filtro woocommerce_variable_children_args
Produtos variáveis com dezenas de variações cada um multiplicam os JOIN. O arquivo de uma loja de roupa com 200 produtos variáveis de 30 variações cada faz, em cada carregamento, um JOIN contra wp_posts para 6000 produtos filhos.
Duas correções práticas:
- Limite as variações carregadas na lista suspensa às que estão em stock e visíveis
- Use
woocommerce_variable_children_argspara excluir do carregamento inicial nos arquivos as variações privadas e sem stock
add_filter( 'woocommerce_variable_children_args', function( $args ) {
if ( is_shop() || is_product_category() ) {
$args['post_status'] = 'publish';
$args['meta_query'][] = array(
'key' => '_stock_status',
'value' => 'instock',
);
}
return $args;
} );Como travar o crescimento das tabelas do Action Scheduler
Todas as extensões do WooCommerce que trabalham em segundo plano usam o Action Scheduler. Subscrições, emails de seguimento, recuperação de carrinhos abandonados, sincronização de stock, integrações com ERP: tudo acaba em wp_actionscheduler_actions e wp_actionscheduler_logs.
Numa loja com subscrições ativas e recuperação de carrinhos, estas tabelas acumulam num ano um grande número de ações concluídas e falhadas, e o painel consulta-as em cada carregamento para mostrar o contador da fila.
Script de manutenção:
# Apagar ações concluídas e canceladas com mais de 31 dias
wp action-scheduler clean --before="31 days ago" --pause=1
# Ver o tamanho da fila
wp db query "SELECT status, COUNT(*) FROM wp_actionscheduler_actions GROUP BY status;"
# Confirmar os índices em status e scheduled_date_gmt
wp db query "SHOW INDEX FROM wp_actionscheduler_actions;"Quanto tempo o Action Scheduler guarda ações concluídas e falhadas
O Action Scheduler limpa o que é seu durante a execução da fila. O seu mecanismo de limpeza da fila apaga as ações concluídas e canceladas quando passam dos 31 dias, e o filtro action_scheduler_retention_period altera esse prazo. Desde o Action Scheduler 4.0 também são apagadas as ações falhadas, por predefinição ao fim de três meses (action_scheduler_retention_period_for_failed); o comentário no código liga esse prazo ao ciclo contabilístico trimestral.
A limpeza corre dentro do processo da fila. Numa loja que desliga o WP-Cron e não tem um cron de sistema a chamar wp-cron.php ou wp action-scheduler run, a fila para e a limpeza para com ela, sem qualquer erro visível. A acumulação deixada por essa falha pode ser apagada à mão com wp action-scheduler clean, que, segundo a referência WP-CLI do Action Scheduler, aceita --status (por predefinição canceled,complete), --before (por predefinição 31 dias), --batch-size (por predefinição 20) e --pause para esperar entre lotes numa base de dados carregada.
Como otimizar as imagens de produto no WooCommerce
As imagens de produto são normalmente a parte mais pesada de qualquer loja. Três regras:
- AVIF para imagens principais e galerias, com WebP como alternativa para versões antigas do Safari que ainda apareçam no tráfego
- Não carregue exportações raw de 4000 px. Limite o carregamento a 2000 px no lado maior
- O
loading="lazy"nativo chega; remova lazy loaders antigos em JavaScript que entrem em conflito com ele
Redimensione as imagens antes de as carregar, em vez de confiar que o WordPress gera miniaturas em tempo real, o que escreve em wp_postmeta e faz crescer a base de dados.
Como limpar a base de dados do WooCommerce com WP-CLI
Execute isto todos os meses, e com mais frequência em lojas com muitos carrinhos abandonados:
# Apagar sessões expiradas (só necessário se a limpeza agendada parou)
wp db query "DELETE FROM wp_woocommerce_sessions WHERE session_expiry < UNIX_TIMESTAMP();"
# Limpar os transients do WooCommerce
wp wc tool run clear_transients --user=1
# Otimizar as tabelas
wp db optimize
# Ver a profundidade da fila do Action Scheduler
wp action-scheduler statusAgende por cron de sistema em vez de wp-cron.php, que só dispara com tráfego. A opção --user em wp wc tool run segue os exemplos da CLI do WooCommerce; use o ID de uma conta de administrador da sua instalação. Quem preferir o painel pode usar o WP-Optimize.
Como o WooCommerce limpa as sessões dos clientes
O WooCommerce já poda a sua tabela de sessões. Regista uma ação recorrente do Action Scheduler, woocommerce_cleanup_sessions, que corre a cada 12 horas (class-woocommerce.php), e o gestor de sessões apaga as linhas expiradas em lotes de 100, com uma pausa curta entre lotes. A sessão de um visitante expira ao fim de dois dias e a de um cliente com sessão iniciada ao fim de sete; os filtros wc_session_expiration e wc_session_expiring alteram os dois prazos. Durações acima de 30 dias deixam um aviso no registo do WooCommerce, porque sessões longas fazem crescer a tabela.
Daqui resultam duas coisas. Uma tabela de sessões medida em gigabytes indica normalmente que a fila do Action Scheduler não está a correr ou que um plugin aumentou a duração das sessões, por isso verifique wp action-scheduler status e esses filtros antes de apagar o que quer que seja. E nunca execute TRUNCATE em wp_woocommerce_sessions numa loja em produção: isso esvazia todos os carrinhos ativos, enquanto o DELETE ... WHERE session_expiry < UNIX_TIMESTAMP() acima só remove linhas já expiradas.
Que métricas de performance do WooCommerce acompanhar
Otimizar às cegas acaba em otimizar a coisa errada. Escolha alguns sinais e acompanhe-os todas as semanas:
- TTFB com cache fria no arquivo da loja e numa página de produto
- Tempo até ao primeiro byte no carrinho e no checkout com um e com quatro artigos no carrinho
- Número de linhas e tamanho total das opções com autoload em wp_options
- Profundidade da fila do Action Scheduler e ação pendente mais antiga
O Query Monitor em staging mostra que consultas dominam. O New Relic ou o Datadog APM em produção mostram a distribuição real. O PageSpeed Insights é um teste de fumo, não um diagnóstico.
Última atualização
2026-10-14. Se a sua loja não encaixa nestes padrões, o problema não está nos padrões. Comece pelo slow query log. Para HPOS, Redis e manutenção da base de dados numa loja em produção, conte com um programador WooCommerce que conheça estes estrangulamentos.







