Ajustar o OPcache no WordPress é dar ao PHP memória compartilhada e espaço para arquivos suficientes para manter em memória o código compilado do core, do tema e de todos os plugins, sem que nenhuma requisição precise recompilar nada. Ajustar o PHP-FPM é rodar quantos workers do PHP a RAM e a CPU do servidor aguentam de verdade, para as requisições não ficarem na fila. Os dois aparecem no tempo de resposta do servidor nas requisições sem cache. Os valores certos dependem do seu código e do seu tráfego: conte os arquivos PHP, leia o opcache_get_status(), meça a memória real dos workers e acompanhe a página de status do FPM, em vez de copiar números de um post qualquer.
Quando um WordPress tem resposta lenta do servidor e o cache de página está em ordem, a camada do PHP é um dos primeiros lugares que olho. O que mais encontro é OPcache cheio e reiniciando, pool com cinco workers num servidor de 16 GB de RAM, ou o contrário: um pool tão grande que joga o servidor no swap no horário de pico. Este artigo passa pelas diretivas que importam e mostra como escolher os valores para o seu servidor. Ele faz parte do meu guia sobre como acelerar o WordPress e continua, do lado do servidor, o que escrevi sobre como reduzir o TTFB no WordPress.
O que o OPcache e o PHP-FPM fazem pelo WordPress?
Toda requisição do WordPress carrega centenas de arquivos PHP: core, tema e todos os plugins ativos, a página usando eles ou não. Sem OPcache, o PHP lê, interpreta e compila cada um desses arquivos em bytecode a cada requisição. O OPcache guarda o bytecode compilado em memória compartilhada, e todos os workers reaproveitam.
O PHP-FPM é o gerenciador de processos que roda esses workers. Cada worker atende uma requisição por vez. Se chegam dez requisições e o pool tem oito workers, duas esperam na fila, e essa espera entra no TTFB delas. Nenhum profiler do WordPress mostra esse tempo, porque o WordPress nem começou a rodar.
Nenhum dos dois ajuda uma página que sai do cache de página, já que o PHP não roda. Eles importam nos cache misses, para usuários logados, no wp-admin, no checkout do WooCommerce, na REST API, no admin-ajax e no cron. São as mesmas requisições em que um Redis object cache ajuda, então os dois trabalhos costumam andar juntos.
Quais diretivas importam para ajustar o OPcache no WordPress?
Estas são as configurações do php.ini que reviso em todo servidor. Os valores padrão listados são os documentados pelo PHP. Sua hospedagem ou distribuição pode vir com outros, então confira com php -i | grep opcache ou com phpinfo() numa requisição web, porque o CLI e o FPM podem carregar arquivos ini diferentes.
| Diretiva | Padrão | O que controla | Como escolher o valor |
|---|---|---|---|
opcache.enable | 1 | OPcache nas requisições web | Tem que estar ligado. Algumas hospedagens ainda entregam desligado |
opcache.memory_consumption | 128 (MB) | Memória compartilhada para os scripts compilados | O suficiente para o free_memory continuar com folga depois de um dia inteiro de tráfego |
opcache.interned_strings_buffer | 8 (MB) | Memória para strings repetidas, como nomes de classes e funções | Aumente se o status mostrar o buffer de interned strings cheio |
opcache.max_accelerated_files | 10000 | Número máximo de scripts em cache | Acima do número de arquivos PHP no servidor, com margem para atualizações |
opcache.validate_timestamps | 1 | Se o PHP verifica alterações nos arquivos | Deixe ligado se o WordPress se atualiza sozinho; veja abaixo |
opcache.revalidate_freq | 2 (segundos) | De quanto em quanto tempo essa verificação acontece | Valor maior significa menos verificações e mais demora para enxergar arquivos alterados |
opcache.save_comments | 1 | Mantém os docblocks no cache | Deixe ligado. Algumas bibliotecas leem anotações em tempo de execução |
Para dimensionar o max_accelerated_files, conte os arquivos PHP de todos os sites que usam o mesmo master do PHP-FPM, porque eles dividem um único OPcache:
find /var/www -type f -name "*.php" | wc -lO PHP arredonda o valor para cima até o próximo número de uma lista interna de primos, então o limite efetivo fica um pouco acima do que você definiu. Um WooCommerce com page builder e quarenta plugins passa fácil dos 10.000 arquivos do padrão.
Como saber se o OPcache tem tamanho suficiente?
Leia o opcache_get_status() numa requisição web, não no CLI, porque o CLI tem um cache separado. Coloque um script pequeno atrás de autenticação ou restrição por IP, abra no navegador e apague em seguida:
<?php
$s = opcache_get_status( false );
printf( "Memoria livre: %.1f MB\n", $s['memory_usage']['free_memory'] / 1048576 );
printf( "Memoria desperdicada: %.1f MB\n", $s['memory_usage']['wasted_memory'] / 1048576 );
printf( "Chaves em cache: %d de %d\n", $s['opcache_statistics']['num_cached_keys'], $s['opcache_statistics']['max_cached_keys'] );
printf( "Taxa de acerto: %.2f%%\n", $s['opcache_statistics']['opcache_hit_rate'] );
printf( "Reinicios por falta de memoria: %d\n", $s['opcache_statistics']['oom_restarts'] );
printf( "Reinicios por falta de slots: %d\n", $s['opcache_statistics']['hash_restarts'] );Os contadores que importam são os de reinício. oom_restarts acima de zero quer dizer que o OPcache ficou sem memória e jogou tudo fora, e as requisições seguintes recompilaram o código inteiro. hash_restarts acima de zero quer dizer que acabaram os slots de arquivo. Qualquer um dos dois é motivo para aumentar a diretiva correspondente. Taxa de acerto alta, sozinha, não prova muita coisa, porque ela é calculada desde o último reinício.
Vale desligar o validate_timestamps no WordPress?
Com opcache.validate_timestamps=0, o PHP nunca verifica se um arquivo mudou. Isso economiza um pouco de trabalho por requisição, e é uma recomendação comum. No WordPress tem um porém: atualizações de core, plugins e temas feitas pelo wp-admin ou automaticamente gravam arquivos novos no disco, e o PHP continua executando o bytecode antigo até alguém resetar o cache. Dá para acabar com código meio atualizado na memória.
Só desligo quando o deploy passa por um pipeline que reseta o OPcache no final, recarregando o PHP-FPM ou chamando opcache_reset() numa requisição web, e quando as atualizações pelo wp-admin estão desativadas. Fora isso, deixo ligado e, se precisar, aumento o revalidate_freq.
O JIT, disponível desde o PHP 8.0, é outra conversa. Ele acelera código que exige muita CPU, e uma requisição típica de WordPress passa boa parte do tempo esperando o banco e o I/O. Se quiser testar, meça o TTFB em requisições sem cache com e sem JIT antes de manter.
Quantos workers do PHP-FPM o WordPress precisa?
Não existe número universal. O teto vem da memória, e o valor útil vem do tráfego e da CPU. As configurações ficam no arquivo do pool, em geral /etc/php/<versão>/fpm/pool.d/www.conf no Debian e no Ubuntu:
pmescolhe o modo.staticmantém um número fixo de workers.dynamicmantém uma faixa entre os limites de servidores ociosos.ondemandsó sobe workers quando chegam requisições e derruba depois dopm.process_idle_timeout, o que economiza memória em site com pouco acesso e acrescenta tempo de inicialização na primeira requisição.pm.max_childrené o limite rígido de workers simultâneos e, portanto, de requisições PHP simultâneas.pm.start_servers,pm.min_spare_serversepm.max_spare_serversdefinem o comportamento do mododynamic.pm.max_requestsrecicla o worker depois de tantas requisições. O padrão 0 nunca recicla. Um valor finito segura vazamentos lentos de memória causados por plugins.
Para definir o pm.max_children, pegue a RAM que sobra para o PHP depois do banco, do Redis, do servidor web e do sistema operacional, e divida pela memória média real de um worker com tráfego normal. O nome do processo muda conforme a distribuição e a versão do PHP:
ps -C php-fpm8.3 -o rss= | awk '{ s += $1; n++ } END { printf "%d workers, media de %.0f MB\n", n, s / n / 1024 }'Use a média medida, não o memory_limit. O limite é um teto para uma requisição descontrolada; a maioria dos workers fica bem abaixo dele. Deixe folga para os picos e confira se o resultado faz sentido para a sua CPU também. Quando as requisições dependem muito de CPU, workers muito acima do número de núcleos só ficam esperando uns pelos outros.
Como saber se o gargalo é o PHP-FPM?
O próprio PHP-FPM conta, basta perguntar. Dois sinais resolvem a maioria dos diagnósticos:
- A linha
server reached pm.max_children settingno log do FPM. Se ela aparece nos horários de pico, houve requisição na fila. - A página de status, ativada com
pm.status_path. Os camposlisten queue,max listen queue,max children reachedeslow requestsmostram se houve espera e com que frequência.
O slow log é a configuração mais útil que a maioria dos servidores não tem. Ele grava um backtrace do PHP para cada requisição que passa de um tempo limite, e você enxerga qual função de qual plugin estava rodando:
; arquivo do pool, por exemplo www.conf
pm.status_path = /fpm-status
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
pm.max_requests = 500Esses valores são exemplos para ajustar, não recomendação. Escolha um limite de slowlog que pegue as requisições que interessam sem lotar o disco, e restrinja o caminho do status ao localhost ou ao seu monitoramento. Recarregue o PHP-FPM depois das mudanças (por exemplo, systemctl reload php8.3-fpm, ajustando o nome do serviço).
Se o slow log aponta sempre para chamadas ao banco, a camada do PHP está bem e o trabalho passa para o autoload do wp_options e a otimização do banco de dados. Se aponta para chamadas HTTP a APIs externas, coloque essas respostas em cache ou mande as chamadas para o cron.
E a versão do PHP?
Rode uma versão do PHP que ainda recebe correções de segurança e que os seus plugins suportam. As versões maiores mais novas, em geral, deixaram o WordPress mais rápido, e as antigas param de receber correções. Teste a compatibilidade em homologação antes, porque plugin velho é o que normalmente trava a atualização. A Saúde do Site avisa quando a sua versão do PHP está desatualizada.
Para ver onde o PHP fica em relação ao cache de página, ao object cache e à CDN, leia qual camada de cache você precisa de verdade. Ajustar o PHP deixa os cache misses mais baratos, mas não substitui o cache.
Perguntas frequentes
Dá para ajustar o OPcache em hospedagem compartilhada?
Normalmente não. A memória do OPcache é definida para o servidor inteiro, e muitas hospedagens compartilhadas não deixam mexer. Você ainda pode conferir se ele está ligado com phpinfo() ou nas configurações de PHP da hospedagem. Se estiver desligado, é motivo para trocar de hospedagem.
Por que o site só fica lento quando tem muito acesso?
Esse padrão aponta para o pool de workers ou para o banco, não para o OPcache. Procure o aviso de pm.max_children no log do FPM e veja na página de status se há fila durante o período lento.
Posso colocar pm em static com um max_children bem alto?
Só se a conta de memória fechar. Worker demais para pouca RAM joga o servidor no swap, e aí tudo fica lento ao mesmo tempo. Meça a memória dos workers primeiro.
Se o tempo de resposta do seu servidor está alto e você quer alguém rastreando a causa no PHP, no OPcache, no banco e nas camadas de cache, com medição de verdade, comece pela minha auditoria de performance WordPress.