Atualizado em 19/06/2026
Toda página do WordPress repete trabalho: consultas ao banco, chamadas a APIs externas, cálculos. O cache no nível da aplicação guarda o resultado desse trabalho para reaproveitá-lo, evitando refazer o que é caro. O WordPress oferece duas ferramentas para isso — a Transients API e o Object Cache (wp_cache_*) — e usá-las bem depende de entender uma diferença central: persistência. Este guia mostra os padrões de uso, onde cada cache mora e as armadilhas (a wp_options inchada, o autoload pesado) que transformam uma otimização em problema.
Transients: cache com expiração
Um transient é um valor nomeado com prazo de validade. A API tem três funções: set_transient( $chave, $valor, $expiracao ), get_transient( $chave ) e delete_transient( $chave ). O padrão de uso é sempre o mesmo — tenta o cache; se vazio, calcula, guarda e retorna:
<?php
/**
* Retorna os posts mais comentados, cacheados por 12 horas.
* Padrão: tenta o cache -> se false, computa -> guarda -> retorna.
*/
function meu_plugin_posts_populares() {
$chave = 'meu_plugin_posts_populares';
// 1) Tenta ler do cache.
$posts = get_transient( $chave );
// 2) get_transient retorna false se não existir OU se já expirou.
if ( false === $posts ) {
// 3) Trabalho caro: só roda quando o cache está vazio.
$posts = get_posts( array(
'orderby' => 'comment_count',
'order' => 'DESC',
'numberposts' => 10,
) );
// 4) Guarda com expiração (constantes de tempo do core).
set_transient( $chave, $posts, 12 * HOUR_IN_SECONDS );
}
return $posts;
}
Pontos a observar:
- Como
get_transient()devolvefalsetanto para "não existe" quanto para "expirou", não guarde o valor booleanofalseem um transient — você nunca saberia distinguir. Para valores assim, prefira o cache de objeto ou armazene um marcador. - Use as constantes de tempo do núcleo:
MINUTE_IN_SECONDS,HOUR_IN_SECONDS,DAY_IN_SECONDS. Expiração0significa "sem expiração" (mas você ainda deve invalidar manualmente). - A chave deve ser curta e única (prefixe com o nome do plugin). Há um limite prático de tamanho para o nome.
Onde os transients ficam (e por que a wp_options incha)
Aqui está o detalhe que separa quem usa transients de quem usa bem. Sem um cache de objeto persistente instalado, cada transient vira duas linhas na tabela wp_options: uma com o valor (_transient_CHAVE) e outra com o timeout (_transient_timeout_CHAVE).
Em um site com muitas chaves dinâmicas (uma por produto, por usuário, por busca), isso gera milhares de linhas. Transients expirados nem sempre são removidos na hora — só quando relidos — então o lixo se acumula. A wp_options cresce, e como parte dela é carregada via autoload em toda requisição, o site desacelera. É uma causa frequente de WooCommerce lento.
Não marque valores grandes como autoload. Tudo com autoload = yes em wp_options é lido de uma só vez em cada requisição. Transients comuns não entram no autoload, mas options criadas com add_option( $chave, $valor, '', 'yes' ), sim — guarde no autoload só o que é pequeno e usado em quase toda página.
A boa notícia: instale um cache de objeto persistente (Redis ou Memcached, via drop-in) e o WordPress passa a gravar os transients nesse backend, em memória, sem tocar na wp_options. O mesmo código funciona, mas a leitura fica mais rápida e a tabela não incha. É a base do que tratamos em cache no WordPress.
Object Cache: evitando trabalho dentro da requisição
O cache de objeto (wp_cache_get, wp_cache_set, wp_cache_delete) guarda valores arbitrários, opcionalmente em grupos. Por padrão ele é não-persistente: existe apenas durante a requisição atual e é descartado ao final. Isso já é útil para não repetir a mesma consulta várias vezes na mesma página:
<?php
function meu_plugin_dados_autor( $autor_id ) {
$grupo = 'meu_plugin';
$chave = 'autor_' . absint( $autor_id );
// $found indica se a chave existia (distingue de um valor null/false legítimo).
$found = false;
$dados = wp_cache_get( $chave, $grupo, false, $found );
if ( false === $found ) {
$dados = meu_plugin_consulta_cara( $autor_id ); // ex.: várias queries
// Sem expiração explícita, dura até o fim da requisição
// (ou até a expiração, se houver backend persistente).
wp_cache_set( $chave, $dados, $grupo, HOUR_IN_SECONDS );
}
return $dados;
}
Repare no parâmetro $found passado por referência: ele resolve a ambiguidade do false que atrapalha os transients — você sabe se a chave existia, mesmo que o valor guardado seja false ou null.
Com um drop-in persistente (Redis/Memcached) instalado, o cache de objeto deixa de morrer no fim da requisição e passa a valer entre requisições e entre processos — e a expiração que você passa em wp_cache_set() finalmente é respeitada de verdade. É aí que wp_cache_* compete diretamente com transients, com a vantagem de não usar o banco.
Invalidando o cache na escrita
Cache que não é invalidado serve dado velho. Sempre que a fonte mudar, apague a chave para forçar o recálculo. O lugar natural é um hook de escrita, como save_post:
<?php
// Quando um post é salvo, invalida o cache dos "posts populares".
add_action( 'save_post', 'meu_plugin_limpar_cache' );
function meu_plugin_limpar_cache( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) {
return; // ignora revisões
}
// Apaga o transient (volta a computar na próxima leitura).
delete_transient( 'meu_plugin_posts_populares' );
// Se também usou cache de objeto, invalide a chave correspondente.
wp_cache_delete( 'autor_' . get_post_field( 'post_author', $post_id ), 'meu_plugin' );
}
Combine as duas estratégias: expiração como rede de segurança (o dado não fica eterno, mesmo se você esquecer de invalidar em algum caminho) e invalidação explícita na escrita (o usuário não vê dado desatualizado).
Multisite: site transients
Em uma rede multisite, os transients comuns são por blog. Para um valor compartilhado por toda a rede, use os site transients: set_site_transient(), get_site_transient() e delete_site_transient(). Sem cache de objeto persistente, eles ficam na wp_sitemeta (rede), não na wp_options de um blog. A semântica é idêntica — só muda o escopo.
Transient × Object Cache: quando usar cada um
| Transient API | Object Cache (wp_cache_*) | |
|---|---|---|
| Persistência padrão | Persistente (vai para wp_options) | Não-persistente (só na requisição) |
| Com Redis/Memcached | Vai para o backend, fora do banco | Torna-se persistente entre requisições |
| Expiração | Sempre suportada | Suportada de fato só com backend persistente |
| Escopo | Por site (ou rede, com site_transient) | Por requisição (ou global, com backend) |
Armazena false com segurança? | Não (ambíguo) | Sim (via parâmetro $found) |
| Quando usar | Resultado que precisa sobreviver entre requisições (API externa, lista pesada) | Evitar repetir o mesmo trabalho dentro de uma requisição |
Resumo da decisão: se você instalou um cache de objeto persistente, os dois convergem e a Transients API é a forma mais portável de escrever (funciona com ou sem Redis). Se não instalou, prefira transients para o que precisa durar entre requisições — e use cache de objeto para deduplicar consultas dentro de uma página, ciente de que ele some no fim. Combinado a uma WP_Query bem escrita, é o jeito mais barato de cortar carga do banco.
Perguntas frequentes
Qual a diferença entre transient e cache de objeto?
O transient tem expiração própria e é potencialmente persistente: sem um cache de objeto persistente instalado, ele é gravado na tabela wp_options e sobrevive entre requisições. O cache de objeto (wp_cache_*) é, por padrão, não-persistente: vive apenas durante uma única requisição e some ao final dela — a não ser que você instale um drop-in (Redis, Memcached). Regra prática: use transient para o que precisa sobreviver entre requisições; use cache de objeto para evitar repetir o mesmo trabalho dentro de uma requisição.
Onde os transients ficam guardados?
Depende do ambiente. Sem cache de objeto persistente, ficam na tabela wp_options (em dois registros por transient: o valor e o timeout). Com um cache de objeto persistente instalado (Redis/Memcached via drop-in object-cache.php), o WordPress redireciona os transients para esse backend e não toca na wp_options. Por isso, em sites sem Redis, transients mal gerenciados podem inchar a wp_options e afetar a performance.
Transient expirado é apagado automaticamente?
Não na hora exata. Quando você chama get_transient() e o valor já expirou, o WordPress retorna false e remove aquele registro. Mas transients expirados que nunca mais são lidos podem permanecer na wp_options por bastante tempo (são limpos por rotinas eventuais). Em lojas movimentadas isso vira lixo acumulado — tema ligado a WooCommerce lento.
O que é o autoload em options e por que importa para cache?
Cada linha de wp_options tem uma coluna autoload. Tudo marcado como autoload = yes é carregado de uma vez, em toda requisição. Transients normais não entram no autoload, mas valores guardados via add_option()/update_option() com autoload ligado, sim — se forem grandes ou numerosos, pesam em cada carregamento. Guarde no autoload apenas o que é pequeno e usado em quase toda página.
Como funcionam os transients em multisite?
Os transients comuns são por site (blog) da rede. Para um valor compartilhado por toda a rede, use os site transients: set_site_transient(), get_site_transient() e delete_site_transient(). Sem cache de objeto persistente, eles vão para a tabela wp_sitemeta em vez da wp_options de um blog específico.
Preciso invalidar o cache manualmente?
Sim, sempre que o dado de origem mudar. Transients expiram por tempo, mas se a informação mudou antes da expiração você serve dado velho. O padrão é apagar a chave (delete_transient() / wp_cache_delete()) em um hook de escrita — por exemplo, save_post ao publicar um post — para que o próximo acesso recompute. Definir expiração curta ajuda como rede de segurança, mas não substitui a invalidação explícita.