Apache e PHP-FPM otimizados para WordPress de alto tráfego

WordPress aguenta alto tráfego, mas só até você trombar nos gargalos clássicos. O problema é que eles quase nunca aparecem onde a galera imagina 😅

Vou listar os gargalos típicos, em ordem de impacto real em produção, com sinais claros e como confirmar cada um.

Otimizar Apache e PHP-FPM para WordPress de alto tráfego exige planejamento cuidadoso de infraestrutura e configuração do servidor. Muitas equipes só começam a ajustar esses parâmetros quando o site já apresenta lentidão ou indisponibilidade. Esse comportamento é típico de ambientes que operam de forma reativa. No guia sobre operação reativa vs operação proativa em servidores, explicamos como equipes podem evoluir para uma gestão mais estratégica da infraestrutura.


1️⃣ Banco de dados (MariaDB/MySQL) — o vilão nº 1

O que acontece

  • Consultas lentas
  • Locks em tabelas
  • Muitas queries repetidas por página

Sintomas

  • load average sobe mesmo com CPU “livre”
  • IOWait aparece sem disco saturado
  • Páginas específicas ficam lentas (home, busca, WooCommerce)

Causas comuns

  • Plugins mal feitos (SEO, estatística, filtros)
  • Falta de índices
  • innodb_buffer_pool_size pequeno
  • ORDER BY RAND(), LIKE '%termo%'

Como confirmar

SHOW PROCESSLIST;
SHOW ENGINE INNODB STATUS\G

2️⃣ PHP-FPM — fila invisível

O que acontece

O tráfego cresce → PHP não consegue atender → requisições ficam esperando.

Sintomas

  • Tempo de resposta alto sem erro
  • 502 / 504 intermitente
  • CPU baixa, site lento

Causas comuns

  • pm.max_children baixo
  • max_execution_time alto demais
  • Scripts PHP pesados (Elementor + Woo + plugins)

Como confirmar

pm.status_path
slowlog

3️⃣ Cache mal configurado (ou inexistente)

O que acontece

Cada visitante gera:

PHP → MySQL → PHP → HTML
em vez de servir HTML pronto.

Sintomas

  • Site cai com picos pequenos
  • Mesmo conteúdo recalculado sempre
  • Home lenta, posts simples também

Causas comuns

  • Cache só no plugin (sem cache real de página)
  • Cache ignorado para usuários anônimos
  • Sem OPcache

Stack ideal

  • Cache de página (Nginx/FastCGI)
  • OPcache
  • Object Cache (Redis)

4️⃣ Plugins — efeito dominó

O que acontece

Um plugin ruim multiplica o custo de cada request.

Sintomas

  • Lentidão após “só instalar mais um plugin”
  • Queries duplicadas
  • Admin lento

Campeões de problema

  • Page builders
  • Plugins de estatística
  • Plugins de busca interna
  • Plugins SEO mal configurados

Como confirmar

  • Query Monitor
  • SAVEQUERIES
  • Slow log do PHP

5️⃣ Nginx / Apache — gargalo silencioso

O que acontece

Servidor web vira fila de espera.

Sintomas

  • Muitas conexões abertas
  • CPU ok, RAM ok, mas lentidão geral
  • netstat cheio

Causas comuns

  • Apache sem proxy
  • KeepAlive mal ajustado
  • Worker insuficiente

Como confirmar

ss -s
apachectl status
nginx stub_status

6️⃣ Disco — mesmo NVMe sofre

O que acontece

WordPress escreve MUITO:

  • Sessões
  • Logs
  • Cache
  • Banco

Sintomas

  • IOWait intermitente
  • Lentidão aleatória
  • Pico ao mesmo tempo todo dia

Causas comuns

  • Banco + site no mesmo disco
  • Sem noatime
  • Swap em SSD lento

Como confirmar

iostat -x

7️⃣ Tráfego externo: bots e crawlers

O que acontece

Bots consomem PHP e DB como usuários reais.

Sintomas

  • Pico fora de horário
  • User-Agents estranhos
  • Googlebot “derruba” site

Causas comuns

  • Sem rate limit
  • Sem cache para bots
  • XML sitemap pesado

8️⃣ Falta de separação de camadas

O erro clássico

“Tenho CPU e RAM sobrando, mas está lento”

O problema real

Tudo roda no mesmo lugar:

  • Web
  • PHP
  • DB
  • Cache
  • Cron

Solução em tráfego alto

  • DB separado
  • Cache fora do PHP
  • Cron real (não WP-Cron)

📌 Regra de ouro do WordPress em alto tráfego

Se o WordPress chega no PHP, você já perdeu desempenho.

Tráfego alto de verdade funciona assim:

Cache (HTML) → Cache de objeto → PHP → DB

Quanto menos requests chegam no PHP, melhor.

para cenário de alto tráfego real, não setup genérico de blog 🙂

Stack: Nginx (proxy) → Apache → PHP-FPM → MariaDB 11.3
Objetivo: máximo throughput, mínima latência, sem “engarrafamento invisível”


🧱 Arquitetura recomendada (fluxo ideal)

[ Cliente ]
     ↓
[ NGINX ]
  ├─ Cache HTML (FastCGI Cache)
  ├─ Rate limit / Bots
  └─ Proxy → Apache
          ↓
      [ Apache ]
        └─ PHP-FPM
             ↓
        Object Cache (Redis)
             ↓
        MariaDB 11.3

👉 Meta: 90%+ das requisições morrem no Nginx, sem tocar PHP.


1️⃣ NGINX (proxy reverso) — peça mais importante

🔹 Função

  • Cache de página
  • Bloquear bots
  • Servir estáticos
  • Reduzir carga do Apache/PHP

🔹 Ajustes críticos

Cache FastCGI

fastcgi_cache_path /var/cache/nginx levels=1:2
keys_zone=WORDPRESS:256m inactive=60m max_size=20g;

fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale updating error timeout invalid_header http_500;

Ignorar cache só quando precisa

set $skip_cache 0;

if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "")   { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in") { set $skip_cache 1; }

➡️ Visitante anônimo = HTML direto


2️⃣ Apache — reduzir ao mínimo

🔹 Regra

Apache não é cache, não é frontend.

🔹 Configuração ideal

  • MPM: event
  • KeepAlive baixo
  • Workers enxutos
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 2

StartServers 2
ServerLimit 32
MaxRequestWorkers 128

👉 Apache só “desempacota” PHP, nada mais.


3️⃣ PHP-FPM — evitar fila invisível

🔹 Pool recomendado (exemplo)

pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15

request_terminate_timeout = 120s

📌 Regra prática

pm.max_children = RAM disponível para PHP / RAM média por processo

🔹 OPcache (obrigatório)

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=100000
opcache.validate_timestamps=0

4️⃣ Redis — obrigatório em alto tráfego

🔹 Uso correto

  • Transients
  • Sessões
  • Queries repetidas

🔹 Configuração base

maxmemory 1gb
maxmemory-policy allkeys-lru

No WordPress:

  • Plugin Redis Object Cache
  • Prefixo único por site

5️⃣ MariaDB 11.3 — onde a maioria erra

🔹 Ajustes essenciais (my.cnf)

[mysqld]
innodb_buffer_pool_size = 6G
innodb_buffer_pool_instances = 6
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2

tmp_table_size = 256M
max_heap_table_size = 256M

max_connections = 300
thread_cache_size = 100

📌 Buffer pool ≈ 60–70% da RAM livre


6️⃣ WordPress — ajustes invisíveis que salvam

🔹 wp-config.php

define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '256M');

define('DISABLE_WP_CRON', true);
define('WP_CACHE', true);

➡️ Use cron real no sistema:

*/5 * * * * php /site/wp-cron.php

Ajustar corretamente parâmetros como número de workers, pools do PHP-FPM e limites de memória ajuda a evitar gargalos antes que eles afetem usuários. Esse tipo de planejamento faz parte de uma gestão proativa de infraestrutura e performance de servidores.

7️⃣ Bots, crawlers e ataques leves

🔹 Rate limit no Nginx

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

location / {
  limit_req zone=one burst=20 nodelay;
}

🔹 Bloquear lixo comum

  • xmlrpc.php (ou limite severo)
  • wp-login.php com rate limit

8️⃣ Monitoramento mínimo (sem frescura)

O que observar SEMPRE

ItemComando
Fila PHPpm.status_path
DB lentoslow_query_log
Cache hit$upstream_cache_status
IOiostat -x
Load realhtop

🎯 Resultado esperado

SituaçãoAntesDepois
Pico de tráfegoPHP caiNginx absorve
GooglebotDerrubaCache
WooCommerceTravaSuportável
CPUOscilaEstável
DBGargaloÚltima camada

Abaixo está um nginx.conf completo, pronto para produção, pensado exatamente para:

  • Nginx como proxy reverso
  • Apache atrás
  • PHP-FPM
  • WordPress de alto tráfego
  • Cache agressivo sem quebrar login/admin
  • Seguro, estável e previsível

👉 É um arquivo base. Depois você só ajusta paths/domínios.


🧩 nginx.conf — WordPress alto tráfego (proxy + cache)

user nginx;
worker_processes auto;
worker_rlimit_nofile 200000;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 8192;
    multi_accept on;
    use epoll;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    # Performance geral
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 15;
    keepalive_requests 1000;
    server_tokens off;

    client_max_body_size 64m;

    # Buffers
    client_body_buffer_size 128k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 16k;

    # Timeouts
    proxy_connect_timeout 5s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    send_timeout 60s;

    # Logs (opcional reduzir)
    access_log off;

    # Gzip
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        application/json
        application/javascript
        text/xml
        application/xml
        application/xml+rss
        text/javascript
        image/svg+xml;

    # Cache FastCGI
    fastcgi_cache_path /var/cache/nginx/wordpress
        levels=1:2
        keys_zone=WORDPRESS:256m
        inactive=60m
        max_size=20g;

    fastcgi_cache_key "$scheme$request_method$host$request_uri";

    fastcgi_cache_use_stale updating error timeout invalid_header http_500 http_503;
    fastcgi_cache_background_update on;
    fastcgi_cache_lock on;

    # Rate limit (bots / ataques leves)
    limit_req_zone $binary_remote_addr zone=wp_limit:10m rate=5r/s;

    # Apache backend
    upstream apache_backend {
        server 127.0.0.1:8080;
        keepalive 32;
    }

    server {
        listen 80 default_server reuseport;
        server_name _;
        root /var/www/html;

        # Status cache (debug)
        add_header X-Cache $upstream_cache_status always;

        # Rate limit geral
        limit_req zone=wp_limit burst=20 nodelay;

        # Arquivos estáticos (não passam por Apache)
        location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|mp4|webm)$ {
            expires 30d;
            access_log off;
            log_not_found off;
            try_files $uri @apache;
        }

        # Bloqueios importantes
        location = /xmlrpc.php {
            deny all;
        }

        # Cache rules
        set $skip_cache 0;

        if ($request_method = POST) {
            set $skip_cache 1;
        }

        if ($query_string != "") {
            set $skip_cache 1;
        }

        if ($request_uri ~* "/wp-admin/|/wp-login.php") {
            set $skip_cache 1;
        }

        if ($http_cookie ~* "wordpress_logged_in|woocommerce_items_in_cart|wp_woocommerce_session") {
            set $skip_cache 1;
        }

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            proxy_cache WORDPRESS;
            proxy_cache_bypass $skip_cache;
            proxy_no_cache $skip_cache;

            proxy_pass http://apache_backend;
        }

        location @apache {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://apache_backend;
        }
    }
}

🔥 O que esse setup faz de verdade

90%+ das requisições morrem no Nginx
✅ Apache só processa o que realmente precisa de PHP
✅ PHP-FPM não cria fila invisível
✅ Bots não derrubam o site
✅ Cache respeita login, carrinho e admin
✅ Estáticos nunca passam por PHP


⚠️ Ajustes obrigatórios antes de usar

1️⃣ Confirme a porta do Apache

server 127.0.0.1:8080;

2️⃣ Confirme o path do cache

mkdir -p /var/cache/nginx/wordpress
chown -R nginx:nginx /var/cache/nginx

3️⃣ Apache NÃO pode escutar na 80


📊 Resultado esperado em produção

SituaçãoResultado
Pico de tráfegoEstável
GooglebotCache
Home< 50ms
PHPQuase ocioso
DBÚltima camada

vamos evoluir esse setup para HTTPS + HTTP/2, sem quebrar cache, sem gambiarras e com ganho real de performance 👍

Abaixo está o mesmo nginx.conf, já adaptado, e depois explico por que cada coisa existe (pra você saber o que não mexer).


🔐 NGINX + HTTPS + HTTP/2 (WordPress alto tráfego)

Compatível com Let’s Encrypt, Apache atrás, cache agressivo e HTTP/2 ativo


🧩 Bloco completo (server HTTP + HTTPS)

🔁 Redirecionamento HTTP → HTTPS

server {
    listen 80 reuseport;
    server_name exemplo.com www.exemplo.com;
    return 301 https://$host$request_uri;
}

🔒 Server HTTPS + HTTP/2

server {
    listen 443 ssl http2 reuseport;
    server_name exemplo.com www.exemplo.com;
    root /var/www/html;

    # Certificados
    ssl_certificate     /etc/letsencrypt/live/exemplo.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/exemplo.com/privkey.pem;

    # TLS moderno e rápido
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # OCSP
    ssl_stapling on;
    ssl_stapling_verify on;

    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    # Headers de segurança (mínimos e seguros)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options SAMEORIGIN always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;

    # Debug de cache
    add_header X-Cache $upstream_cache_status always;

    # Rate limit
    limit_req zone=wp_limit burst=20 nodelay;

    # Estáticos
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|mp4|webm)$ {
        expires 30d;
        access_log off;
        log_not_found off;
        try_files $uri @apache;
    }

    # Bloqueio XML-RPC
    location = /xmlrpc.php {
        deny all;
    }

    # Cache rules
    set $skip_cache 0;

    if ($request_method = POST) {
        set $skip_cache 1;
    }

    if ($query_string != "") {
        set $skip_cache 1;
    }

    if ($request_uri ~* "/wp-admin/|/wp-login.php") {
        set $skip_cache 1;
    }

    if ($http_cookie ~* "wordpress_logged_in|woocommerce_items_in_cart|wp_woocommerce_session") {
        set $skip_cache 1;
    }

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;

        proxy_cache WORDPRESS;
        proxy_cache_bypass $skip_cache;
        proxy_no_cache $skip_cache;

        proxy_pass http://apache_backend;
    }

    location @apache {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://apache_backend;
    }
}

⚙️ Ajustes obrigatórios no Apache (ATRÁS do HTTPS)

Apache deve “saber” que está em HTTPS

SetEnvIf X-Forwarded-Proto https HTTPS=on

📌 Sem isso:

  • Cookies errados
  • Login quebrado
  • Redirect loop

⚙️ Ajustes obrigatórios no WordPress

wp-config.php

define('FORCE_SSL_ADMIN', true);

if (
    isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
    $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'
) {
    $_SERVER['HTTPS'] = 'on';
}

🚀 Por que HTTP/2 aqui realmente ajuda

Ajuda de verdade quando:

✔ Muitas imagens
✔ CSS/JS fragmentado
✔ Alta latência (mobile, 4G)

Não ajuda quando:

✖ Página já vem cacheada em HTML único
✖ Backend lento (HTTP/2 não salva PHP)

👉 HTTP/2 melhora entrega, não processamento.


🧠 Erros comuns (evite)

❌ Ativar HTTP/2 no Apache (inútil aqui)
❌ Cachear wp-admin
❌ Forçar cipher antigo
❌ Misturar SSL no Apache e no Nginx


📊 Testes pós-setup (obrigatório)

curl -I https://exemplo.com

Verifique:

  • HTTP/2 200
  • X-Cache: HIT

revisar Apache + PHP-FPM pra fechar o ciclo

Perfeito — agora vamos fechar o ciclo e tirar todo o peso inútil do Apache e do PHP-FPM, deixando eles só para o que realmente não pode ser cacheado.
Isso é exatamente onde WordPress de alto tráfego costuma ganhar 30–60% de folga.

Vou assumir CloudLinux 8, Apache atrás do Nginx, PHP-FPM e tráfego alto.


🔧 Apache (atrás do Nginx) — mínimo, previsível e estável

🎯 Objetivo do Apache

  • NÃO segurar conexões
  • NÃO servir estáticos
  • NÃO fazer SSL
  • Apenas despachar PHP para o PHP-FPM

1️⃣ MPM correto (obrigatório)

Use MPM event

apachectl -M | grep mpm

Se não for event:

dnf install mod_mpm_event

Configuração recomendada

<IfModule mpm_event_module>
    StartServers             2
    MinSpareThreads         25
    MaxSpareThreads         75
    ThreadLimit             64
    ThreadsPerChild         25
    MaxRequestWorkers      128
    MaxConnectionsPerChild 5000
</IfModule>

📌 Regra de ouro: Apache nunca deve virar fila.


2️⃣ KeepAlive curto (ou vira armadilha)

KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 2

👉 O Nginx segura conexão longa, o Apache não.


3️⃣ Desligar o que não serve pra nada

Remova módulos inúteis

apachectl -M

Pode remover:

  • status
  • autoindex
  • negotiation
  • dav
  • userdir
  • cgid

Menos módulos = menos memória = mais estabilidade.


4️⃣ Logs controlados (IO importa)

LogLevel warn
CustomLog /var/log/httpd/access_log combined

Se possível, desligar access_log em produção pesada.


🧠 PHP-FPM — onde a fila invisível nasce

🎯 Objetivo

  • Processos suficientes
  • Sem swap
  • Sem fila silenciosa
  • Scripts lentos identificáveis

5️⃣ Pool PHP-FPM (exemplo sólido)

/etc/php-fpm.d/www.conf

pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15

pm.max_requests = 500
request_terminate_timeout = 120s

📌 Como calcular pm.max_children

RAM disponível para PHP / RAM média por processo

👉 Normalmente 80–150 MB por processo em WP pesado.


6️⃣ Slowlog (obrigatório em produção)

request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/slow.log

🔥 Isso mostra:

  • Plugins ruins
  • Queries lentas
  • Funções pesadas

7️⃣ OPcache (não negociável)

/etc/php.d/10-opcache.ini

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=100000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.fast_shutdown=1

⚠️ Em update de plugin/tema:

systemctl reload php-fpm

8️⃣ PHP.ini — pequenos ajustes que evitam queda

memory_limit = 256M
max_execution_time = 120
max_input_time = 60
post_max_size = 64M
upload_max_filesize = 64M

🧪 Testes pós-ajuste (obrigatórios)

Apache

apachectl status

✔ Workers livres
✔ Nenhuma fila

PHP-FPM

curl http://127.0.0.1/status

idle processes > 0
listen queue = 0

Carga real

ab -n 1000 -c 50 https://exemplo.com/

✔ X-Cache: HIT
✔ PHP quase ocioso


🎯 Resultado final da stack

CamadaEstado
NginxCache + HTTP/2
ApacheDespachante
PHP-FPMControlado
RedisCache quente
MariaDBÚltima camada
WPEstável

👉 Agora o gargalo só aparece quando o tráfego é realmente absurdo — e aí já é escala horizontal, não tuning.

Infraestruturas preparadas para alto tráfego não dependem apenas de hardware mais potente, mas de planejamento e monitoramento contínuo. Equipes que adotam essa abordagem conseguem abandonar o modelo de operação reativa e evoluir para uma administração proativa de servidores.

FAQ

O Apache é bom para WordPress de alto tráfego?

Sim. Quando combinado com PHP-FPM e cache, o Apache funciona muito bem em ambientes de alto tráfego.

PHP-FPM é obrigatório para WordPress?

Na prática sim. Ele permite controlar melhor os processos PHP e melhora muito o desempenho.

Quantos processos PHP um servidor pode ter?

Depende da memória disponível e do consumo médio de cada processo PHP.

Redis melhora o desempenho do WordPress?

Sim. Redis reduz queries no banco e melhora o tempo de resposta.

Cache HTTP ainda é necessário com PHP-FPM?

Sim. Mesmo com PHP-FPM, o cache HTTP reduz drasticamente o processamento do servidor.

Veja Mais:

Operação reativa vs proativa: diferenças, riscos e boas práticas
Backup de Servidores Web: Guia de Estratégia e Otimização 2026
Como Otimizar Nextcloud para Grandes Equipes: Performance e Escalabilidade
Alertas que Antecipam Falhas em Servidores