Hands-On · WordPress Hardening

Protegendo seu WordPress com Cloudflare

Este guia une o processo de entrada no Cloudflare com uma camada prática de hardening para WordPress. Primeiro organizamos DNS, proxy e TLS. Depois criamos regras de WAF com critérios claros, ação definida e espaço para testar antes de bloquear algo legítimo.

DNS Proxy Full (strict) WAF Rate limiting WP-Cron WordPress
Regra de ouro O WAF só protege requisições que passam pelo proxy do Cloudflare. Antes de criar bloqueios, confirme que o hostname web está como Proxied e que o servidor de origem aceita HTTPS corretamente.
Roteiro do artigo

Da criação da conta até regras que reduzem a superfície exposta do WordPress.

01

O que o Cloudflare muda na proteção do WordPress

O Cloudflare fica entre o visitante e o servidor de origem. Quando o registro web está em Proxied, as requisições HTTP e HTTPS passam pela rede da Cloudflare antes de chegar à hospedagem. É nesse ponto que entram cache, proteção contra DDoS, regras de WAF, limitação de requisições e outros controles de borda.

ORIGEM

Menos exposição direta

Com o proxy ativo, o visitante recebe endereços da Cloudflare em vez do IP público do servidor. Isso não substitui firewall de origem, mas reduz a exposição do host web.

TRÁFEGO

Filtro antes do WordPress

Regras personalizadas e regras gerenciadas podem bloquear, desafiar ou limitar requisições antes que PHP, plugins e banco de dados precisem processá-las.

CONTROLE

Decisão por contexto

Host, caminho, método, IP, país, bot verificado e User-Agent ajudam a definir uma política que combina com o tipo de site e com o público real.

02

Criar conta e adicionar o domínio

Os painéis da Cloudflare evoluem com frequência, mas a sequência continua a mesma: criar a conta, informar o domínio, revisar os registros DNS importados e apontar os nameservers no registrador. As imagens abaixo preservam o passo a passo original como referência visual.

02.01

Criar uma conta no Cloudflare

Acesse o site da Cloudflare, crie a conta com um e-mail corporativo ou controlado pela empresa e use uma senha forte. Assim que a conta existir, ative MFA antes de configurar domínios ou permissões de equipe.

1Use um e-mail que permaneça sob controle da empresa.
2Ative MFA na conta administrativa antes de criar regras.
3Evite uma conta compartilhada como única administradora do domínio.
02.02

Adicionar seu site

No painel, escolha adicionar um site e informe somente o domínio raiz, sem caminho ou protocolo. Exemplo: lluciox.space, e não https://lluciox.space/wp-admin. Depois selecione o plano adequado e revise a zona DNS importada.

02.03

Trocar os nameservers no registrador

O Cloudflare exibirá dois nameservers próprios para sua zona. A alteração acontece no registrador do domínio, não no provedor de hospedagem. Copie os dois endereços exatamente como aparecem no painel e não remova registros DNS necessários durante a migração.

03

DNS, origem e Proxy Status

Crie ou valide os registros que levam o site para a hospedagem. Para o site WordPress, normalmente isso significa um registro A, AAAA ou CNAME para o hostname público. E-mail, verificações de domínio e serviços que não aceitam proxy devem permanecer em DNS-only.

DNS ONLY

Validar a origem primeiro

Enquanto prepara o certificado no servidor, o modo DNS-only ajuda a testar se a origem responde pelo hostname em HTTPS. Ele não recebe WAF, cache ou proteção de borda e revela o IP de origem em consultas DNS.

PROXIED

Colocar o host web atrás do Cloudflare

Depois de validar a origem em HTTPS, ative a nuvem laranja para os hostnames que servem o site. A partir daí, as regras WAF passam a ser avaliadas antes de a requisição alcançar o WordPress.

04

SSL/TLS: a conexão precisa estar protegida até a origem

O cadeado no navegador cobre apenas a conexão entre visitante e Cloudflare. Para evitar que a segunda etapa fique fraca, entre Cloudflare e hospedagem, a configuração recomendada é Full (strict): HTTPS na origem, certificado válido, não expirado e compatível com o hostname.

EVITAR

Off e Flexible

Off deixa as conexões sem HTTPS. Flexible cifra até a Cloudflare, mas usa HTTP entre Cloudflare e origem. Para WordPress com login, dados pessoais ou painel administrativo, não é o modo adequado.

TRANSIÇÃO

Full

Usa HTTPS para a origem, mas não valida o certificado apresentado. É útil apenas enquanto você corrige a origem para atender os requisitos de modo estrito.

RECOMENDADO

Full (strict)

Exige certificado de origem válido, não expirado, emitido por autoridade pública ou Cloudflare Origin CA e com CN ou SAN correspondente ao hostname.

04.01

Edge Certificates, HTTPS e HSTS

Ative redirecionamento para HTTPS somente depois de confirmar que o site responde corretamente com certificado válido. HSTS merece atenção especial: uma vez publicado com subdomínios e preload, erros de certificado ficam mais difíceis de contornar do lado do visitante.

1Teste HTTPS em domínio e subdomínios antes de ativar HSTS com subdomínios.
2Comece com um período moderado e aumente após validar a operação.
3Ative preload apenas quando todos os requisitos do domínio estiverem maduros.
04.02

Versão mínima de TLS e instalação do WordPress

Defina TLS 1.2 ou superior para reduzir compatibilidade com protocolos antigos. Depois, com DNS e TLS organizados, finalize a instalação do WordPress. A partir daqui o ambiente está pronto para receber as regras de camada 7.

05

Criando regras no WAF para proteger seu WordPress

Daqui em diante, deixamos os prints de lado e trabalhamos com a configuração atual da Cloudflare. No painel novo, procure Security rules; no menu clássico, o caminho pode aparecer como Security → WAF → Custom rules. Crie cada política, cole a expressão em Edit expression, escolha a ação e defina a ordem com intenção.

01GEOBLOCK_HIGH_RISKAplica a postura geográfica antes de regras mais específicas.
02BAD_USER_AGENTSReduz scanners, automações e rotas de diagnóstico não necessárias.
03LOCKDOWN_WP_COREFecha pontos clássicos de enumeração, execução e exposição WordPress.
04BLOCK_WPADMINRestringe login e administração por país ou IP confiável.
05BLOCK_SENSITIVE_FILESEvita acesso a arquivos, repositórios, logs e backups expostos.
05.01

GEOBLOCK_HIGH_RISK

Esta política é uma postura de risco contextual, não uma lista universal de países “maliciosos”. No exemplo, o bloqueio de determinados continentes, Tor e países foi escolhido conforme o perfil de acesso e os IPs que vinham gerando ruído no ambiente. Para sites com audiência internacional, e-commerce ou usuários viajando, comece com Managed Challenge ou monitore antes de bloquear.

custom-rule · action:block · order:01
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
not cf.client.bot
and
not lower(http.request.uri.path) contains "/.well-known/acme-challenge/"
and
(
  (
    ip.src.continent in {"AF" "AS" "T1"}
    and not ip.src.country in {"AO" "MZ"}
  )
  or ip.src.country in {"RU" "IE"}
)
HostImpede que a regra alcance outros domínios presentes na conta.
cf.client.botPreserva bots reconhecidos pela Cloudflare, como mecanismos de busca verificados.
ACMENão interfere no desafio usado para emissão e renovação de certificados.
T1É o código da Cloudflare para tráfego associado à rede Tor.
05.02

BAD_USER_AGENTS

Esta regra intercepta User-Agents vazios e assinaturas comuns de scanners, frameworks de automação e ferramentas de enumeração. Ela também bloqueia as rotas /cdn-cgi/trace e /cli neste exemplo. É uma camada de redução de ruído: um atacante pode alterar o User-Agent, portanto ela não substitui as demais políticas.

custom-rule · action:block · order:02
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
(
  lower(http.request.uri.path) eq "/cdn-cgi/trace"
  or lower(http.request.uri.path) eq "/cli"
  or
  (
    not cf.client.bot
    and
    (
      http.user_agent eq ""
      or lower(http.user_agent) contains "wpscan"
      or lower(http.user_agent) contains "sqlmap"
      or lower(http.user_agent) contains "nmap"
      or lower(http.user_agent) contains "masscan"
      or lower(http.user_agent) contains "nikto"
      or lower(http.user_agent) contains "acunetix"
      or lower(http.user_agent) contains "nessus"
      or lower(http.user_agent) contains "metasploit"
      or lower(http.user_agent) contains "hydra"
      or lower(http.user_agent) contains "zgrab"
      or lower(http.user_agent) contains "gobuster"
      or lower(http.user_agent) contains "dirbuster"
      or lower(http.user_agent) contains "ffuf"
      or lower(http.user_agent) contains "fuzzer"
      or lower(http.user_agent) contains "nuclei"
      or lower(http.user_agent) contains "whatweb"
      or lower(http.user_agent) contains "scrapy"
      or lower(http.user_agent) contains "scraper"
      or lower(http.user_agent) contains "libwww-perl"
      or lower(http.user_agent) contains "python-requests"
      or lower(http.user_agent) contains "aiohttp"
      or lower(http.user_agent) contains "go-http-client"
      or lower(http.user_agent) contains "curl"
      or lower(http.user_agent) contains "wget"
      or lower(http.user_agent) contains "headlesschrome"
      or lower(http.user_agent) contains "phantomjs"
      or lower(http.user_agent) contains "selenium"
    )
  )
)
Bloqueia fácilFerramentas usadas com a assinatura padrão deixam de alcançar a aplicação.
Não confia demaisUser-Agent é controlável pelo cliente e pode ser falsificado em segundos.
Monitore integraçõescurl, wget e bibliotecas HTTP podem ser usados por health checks e automações legítimas.
Bots verificadosA condição preserva os bots conhecidos pela Cloudflare na parte baseada em User-Agent.
05.03

LOCKDOWN_WP_CORE

A terceira regra concentra caminhos e parâmetros que normalmente não precisam ficar abertos ao público. Ela bloqueia XML-RPC, enumeração de autores, rotas de usuários da REST API, tentativa de executar PHP em diretórios sensíveis e caminhos usuais de backup.

custom-rule · action:block · order:03
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
(
  lower(http.request.uri.path) eq "/xmlrpc.php"
  or lower(http.request.uri.query) contains "author="
  or lower(http.request.uri.path) contains "/wp-json/wp/v2/users"
  or lower(http.request.uri.query) contains "rest_route=/wp/v2/users"
  or (
    lower(http.request.uri.path) contains "/wp-content/uploads/"
    and lower(http.request.uri.path) contains ".php"
  )
  or (
    lower(http.request.uri.path) contains "/wp-includes/"
    and lower(http.request.uri.path) contains ".php"
  )
  or starts_with(lower(http.request.uri.path), "/wp-content/backup")
  or starts_with(lower(http.request.uri.path), "/wp-content/backups")
  or starts_with(lower(http.request.uri.path), "/wp-content/backup-db")
  or starts_with(lower(http.request.uri.path), "/wp-content/db-backup")
  or starts_with(lower(http.request.uri.path), "/wp-content/mu-plugins")
)
XML-RPCReduz uma superfície frequentemente explorada para tentativas de autenticação e pingbacks, desde que você não dependa dele.
Autores e usuáriosDiminui enumeração de usuários por query string ou endpoint REST público.
PHP em uploadsEvita chamadas diretas a possíveis arquivos PHP enviados indevidamente para mídia.
BackupsFecha nomes de diretórios recorrentes onde cópias do site podem ter sido esquecidas.
05.04

BLOCK_WPADMIN: restrinja administração sem se bloquear

A expressão abaixo é o bloqueio completo de acesso público a /wp-login.php e /wp-admin, preservando apenas /wp-admin/admin-ajax.php para reduzir quebra de recursos no front-end. Sozinha, ela também bloqueia você. Use-a apenas quando o painel for acessado por uma camada alternativa, como VPN, Cloudflare Access ou uma política de exceção anterior validada.

custom-rule · action:block · order:04
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
(
  lower(http.request.uri.path) eq "/wp-login.php"
  or lower(http.request.uri.path) eq "/wp-login"
  or starts_with(lower(http.request.uri.path), "/wp-login/")
  or lower(http.request.uri.path) eq "/wp-admin"
  or
  (
    starts_with(lower(http.request.uri.path), "/wp-admin/")
    and lower(http.request.uri.path) ne "/wp-admin/admin-ajax.php"
  )
)
VARIANTE A

Permitir somente acessos do Brasil

Útil para uma operação local. Quem estiver fora do Brasil, inclusive você em viagem ou usando VPN de outro país, será bloqueado no login e no painel.

custom-rule · action:block
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
ip.src.country ne "BR"
and
(
  lower(http.request.uri.path) eq "/wp-login.php"
  or lower(http.request.uri.path) eq "/wp-login"
  or starts_with(lower(http.request.uri.path), "/wp-login/")
  or lower(http.request.uri.path) eq "/wp-admin"
  or
  (
    starts_with(lower(http.request.uri.path), "/wp-admin/")
    and lower(http.request.uri.path) ne "/wp-admin/admin-ajax.php"
  )
)
VARIANTE B

Permitir somente seus IPs confiáveis

É a opção mais restritiva. Troque os IPs de documentação pelos seus IPv4 e IPv6 públicos reais. Se seu provedor muda o IP frequentemente, mantenha um método alternativo de recuperação antes de ativar.

custom-rule · action:block
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
not ip.src in {203.0.113.10 2001:db8:1234::/48}
and
(
  lower(http.request.uri.path) eq "/wp-login.php"
  or lower(http.request.uri.path) eq "/wp-login"
  or starts_with(lower(http.request.uri.path), "/wp-login/")
  or lower(http.request.uri.path) eq "/wp-admin"
  or
  (
    starts_with(lower(http.request.uri.path), "/wp-admin/")
    and lower(http.request.uri.path) ne "/wp-admin/admin-ajax.php"
  )
)
05.05

WP-CRON: manter público ou bloquear?

/wp-cron.php não é um arquivo de configuração nem uma página administrativa. Ele é o ponto usado pelo WordPress para processar tarefas agendadas. Publicações programadas, verificações de atualização, e-mails, limpezas de cache, rotinas de backup e jobs de plugins podem depender dessa fila. No comportamento padrão, o WordPress verifica eventos vencidos durante as visitas ao site e pode iniciar uma chamada não bloqueante para /wp-cron.php. Portanto, bloquear a rota sem substituir esse mecanismo pode fazer tarefas pararem sem um erro visível para quem navega no site.

NÃO APAGUENunca exclua fisicamente wp-cron.php. A decisão aqui é sobre permitir ou bloquear o acesso HTTP à rota.
MANTERUse o comportamento padrão quando a hospedagem não oferece agendador confiável, você não controla o servidor ou ainda não validou uma alternativa.
SUBSTITUIREm VPS, servidor dedicado ou hospedagem com cron real, rode a fila pelo sistema e ganhe previsibilidade.
BLOQUEAR DEPOISO bloqueio no WAF só vem depois de o agendador alternativo executar e os jobs críticos serem testados.
OPÇÃO A · MANTER O WP-CRON PADRÃO

Quando não há um agendador real pronto, mantenha a rota funcional.

Esta é a escolha adequada para hospedagens compartilhadas sem cron de sistema confiável, ambientes em que você não tem acesso a WP-CLI ou situações em que ainda não foi possível testar os plugins que usam tarefas agendadas. Nesse cenário, não adicione /wp-cron.php à regra de arquivos sensíveis e não use uma regra Block para a rota.

rate-limit-rule · opção de contenção
http.host in {"lluciox.space" "www.lluciox.space"}
and
http.request.uri.path eq "/wp-cron.php"
CharacteristicIP source

Conta chamadas repetidas do mesmo endereço de origem.

Period60 segundos

Janela inicial para identificar repetição anormal.

Requests30 por período

Valor inicial para conter abuso sem bloquear a operação comum.

Mitigation5 minutos

Bloqueio temporário para a origem que ultrapassar o limite.

OPÇÃO B · USAR AGENDADOR DO SERVIDOR

Substitua o disparo por visitas por um cron real e só então bloqueie o endpoint público.

Essa é a arquitetura mais previsível para VPS, servidores dedicados e hospedagens que liberam tarefas agendadas. O cron do sistema chama o WordPress em um intervalo definido, mesmo quando não há visitantes. Primeiro teste o comando manualmente. Depois agende-o. Somente no final desative o disparo padrão e bloqueie a rota pública.

teste manual · wp-cli
cd /var/www/seu-site && wp cron event run --due-now
crontab · a cada 5 minutos
*/5 * * * * cd /var/www/seu-site && /usr/local/bin/wp cron event run --due-now --quiet >/dev/null 2>&1
wp-config.php · depois do cron testado
define( 'DISABLE_WP_CRON', true );
custom-rule · action:block · condicional
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
lower(http.request.uri.path) eq "/wp-cron.php"
05.06

BLOCK_SENSITIVE_FILES

Esta regra impede a leitura de arquivos que nunca deveriam ser públicos: arquivos de configuração, logs, repositórios de versionamento, manifestos de dependências e cópias de backup. Ela também identifica arquivos compactados associados a nomes que costumam indicar backup, banco de dados ou diretório antigo. /wp-cron.php fica fora desta lista porque exige uma decisão de arquitetura própria: mantenha a rota enquanto usa WP-Cron padrão ou bloqueie-a somente depois de migrar para um agendador real e validar as tarefas.

custom-rule · action:block · order:05
lower(http.host) in {"lluciox.space" "www.lluciox.space"}
and
(
  lower(http.request.uri.path) in {"/wp-load.php" "/wp-settings.php" "/.htaccess" "/.htpasswd" "/.env" "/wp-config.php" "/composer.json" "/composer.lock" "/package.json" "/package-lock.json" "/yarn.lock" "/pnpm-lock.yaml" "/debug.log" "/error_log" "/php_errorlog" "/php-errors.log" "/readme.html" "/readme.txt" "/readme.md" "/license.txt" "/license.md" "/licence.txt" "/licence.md" "/changelog.txt" "/changelog.md"}
  or lower(http.request.uri.path) contains "/.env"
  or lower(http.request.uri.path) contains "/.git"
  or lower(http.request.uri.path) contains "/.svn"
  or lower(http.request.uri.path) contains "/.hg"
  or lower(http.request.uri.path) contains "/.idea"
  or lower(http.request.uri.path) contains "/.vscode"
  or lower(http.request.uri.path) contains "/wp-config.php"
  or lower(http.request.uri.path) contains "/composer.json"
  or lower(http.request.uri.path) contains "/composer.lock"
  or lower(http.request.uri.path) contains "/debug.log"
  or lower(http.request.uri.path) contains "/error_log"
  or ends_with(lower(http.request.uri.path), ".sql")
  or ends_with(lower(http.request.uri.path), ".bak")
  or ends_with(lower(http.request.uri.path), ".bkp")
  or ends_with(lower(http.request.uri.path), ".backup")
  or ends_with(lower(http.request.uri.path), ".bk")
  or ends_with(lower(http.request.uri.path), ".old")
  or ends_with(lower(http.request.uri.path), ".orig")
  or ends_with(lower(http.request.uri.path), ".save")
  or ends_with(lower(http.request.uri.path), ".swp")
  or ends_with(lower(http.request.uri.path), ".tmp")
  or (
    (
      lower(http.request.uri.path) contains "/backup"
      or lower(http.request.uri.path) contains "/dump"
      or lower(http.request.uri.path) contains "/database"
      or lower(http.request.uri.path) contains "/db"
      or lower(http.request.uri.path) contains "/banco"
      or lower(http.request.uri.path) contains "/mysql"
      or lower(http.request.uri.path) contains "/public_html"
      or lower(http.request.uri.path) contains "/site"
      or lower(http.request.uri.path) contains "/wordpress"
      or lower(http.request.uri.path) contains "/wp-content"
      or lower(http.request.uri.path) contains "/old"
      or lower(http.request.uri.path) contains "/antigo"
    )
    and
    (
      ends_with(lower(http.request.uri.path), ".zip")
      or ends_with(lower(http.request.uri.path), ".tar")
      or ends_with(lower(http.request.uri.path), ".tgz")
      or ends_with(lower(http.request.uri.path), ".gz")
      or ends_with(lower(http.request.uri.path), ".7z")
      or ends_with(lower(http.request.uri.path), ".rar")
    )
  )
)
05.07

RATE_LIMIT_CRITICAL_WP

Rate limiting não avalia uma requisição isolada; ele mede repetição dentro de uma janela de tempo. Essa política aponta para login, XML-RPC, envio de comentários, enumeração de usuários e caminhos de plugin que costumam ser sondados. Configure a característica por IP source, a janela, o número de requisições e a duração da mitigação de acordo com o comportamento normal do seu site.

rate-limit-rule · action:block
http.host in {"lluciox.space" "www.lluciox.space"}
and
(
  http.request.uri.path eq "/wp-login.php"
  or http.request.uri.path eq "/xmlrpc.php"
  or http.request.uri.path eq "/wp-comments-post.php"
  or http.request.uri.path contains "/wp-json/wp/v2/users"
  or
  (
    http.request.uri.path contains "/wp-content/plugins/"
    and
    (
      http.request.uri.path contains ".php"
      or http.request.uri.path contains "readme.txt"
      or http.request.uri.path contains "changelog"
      or http.request.uri.path contains ".bak"
      or http.request.uri.path contains ".old"
      or http.request.uri.path contains ".txt"
    )
  )
)
CharacteristicIP source

Conta requisições por endereço de origem.

Period60 segundos

Janela inicial simples para observar o comportamento.

Requests10 por período

Ponto de partida conservador para endpoints críticos.

Mitigation10 minutos

Tempo de bloqueio depois que o limite for atingido.

05.08

Validar a política antes de chamar de proteção

O resultado esperado não é apenas ver eventos bloqueados. Uma configuração madura mantém o site, formulários, login autorizado, cache, renovação de certificado e integrações funcionando, enquanto reduz caminhos desnecessários e tráfego automatizado de baixo valor.

01Verifique o ProxyHost web em Proxied; MX, TXT e serviços de terceiros em DNS-only quando necessário.
02Teste Full (strict)Origem responde em 443 com certificado válido para domínio e subdomínios usados.
03Ative uma regra por vezUse janela privada, eventos de segurança e testes de funcionalidade para cada mudança.
04Revise os bloqueiosEventos recorrentes podem indicar bot, integração legítima ou regra ampla demais.
05Valide o WP-CronConfirme publicação agendada, e-mails, backups e tarefas de plugins antes e depois de qualquer bloqueio da rota.
06Mantenha o WordPress atualizadoWAF complementa, mas não substitui atualização, backup, MFA e menor privilégio.
06

Fontes oficiais para manter a configuração atualizada

As telas do Cloudflare podem mudar, mas as regras devem ser revisadas com a documentação oficial. Consulte principalmente as páginas de custom rules, rate limiting, status de proxy, modos SSL/TLS e regras gerenciadas.

Foto de Leandro Lucio
Sobre o autor

Leandro Lucio

Especialista em Cyber Security e Digital Forensics, com mais de 15 anos de experiência em segurança ofensiva, investigação digital, resposta a incidentes e gestão de vulnerabilidades.

Pentest Forense Digital Resposta a Incidentes Threat Investigation Vulnerabilidades
Conhecer o Perito Lucio