XML-RPC do WordPress: 3 formas de desativar & como identificar ataques ao xmlrpc.php | Exceção do Jetpack

Aviso de afiliados v3.1
Aviso de afiliados v3.2 (somente celular)

Se o log de acesso do seu site WordPress mostra centenas de requisições ao xmlrpc.php seguidas vindas do mesmo IP, é uma tentativa de adivinhar senhas pelo XML-RPC do WordPress. Classifiquei 8.624 registros de um dia de log de acesso de um servidor Nginx + Apache que hospeda vários sites WordPress, apliquei eu mesmo as três formas de desativar e conferi os códigos de resposta, e resumi em qual camada é preciso bloquear para reduzir também a execução de PHP.

O XML-RPC é um protocolo que troca documentos XML via HTTP para chamar funções do WordPress remotamente e, desde o WordPress 3.5, não existe mais a opção de ligar e desligar na tela de configurações, então ele já funciona ativado logo após a instalação.

1️⃣ O papel do xmlrpc.php e quem o usa

Depois que a REST API surgiu, é fácil achar que o [xmlrpc.php] é um arquivo que ninguém usa mais, mas o Jetpack ainda conecta o site ao WordPress.com por meio dele. A documentação oficial do Jetpack diz claramente que ele depende da conexão XML-RPC, e em hospedagens que bloqueiam o XML-RPC por padrão a própria conexão do Jetpack falha. É como um escritório que ainda mantém um número de fax.

No WordPress 7.1, esse arquivo aceita 77 métodos: 50 da família wp, que cuidam de posts, mídia e comentários, 23 dos antigos protocolos de blog (MetaWeblog, MovableType, Blogger), 2 de pingback e 2 demo para testes.

A família wp faz login com o nome de usuário e a senha que vão no corpo da requisição, por isso não passa pela tela de login. Escrever e editar posts, enviar mídia e gerenciar comentários são todos dessa família, e os 23 métodos dos antigos protocolos de blog foram mantidos para trocar posts do mesmo jeito que outras ferramentas de blog.

O demo.sayHello é uma chamada de teste que vem no XML-RPC do WordPress. Ao enviá-la, ele devolve só uma palavra, [Hello!], e, como não exige login, serve para verificar se o XML-RPC está funcionando. O par dele, demo.addTwoNumbers, soma dois números e devolve o resultado.

Todos só respondem a um corpo XML enviado por POST; se você abrir o endereço no navegador, volta um 405 com a mensagem [XML-RPC server accepts POST requests only.].

Os pingbacks também são função desse arquivo. Ao publicar um post, um pingback vai para o xmlrpc.php dos sites linkados no texto, e o servidor que recebe abre ele mesmo o endereço de origem para conferir se o link existe de fato.

🧭 Requisições ao xmlrpc.php e como são tratadas
Segurança do wp-adminDiagrama explicativo
📨 Remetente
↓
Entrada
xmlrpc.php
Processa só o corpo XML enviado por POST
405 · XML-RPC server accepts POST requests only.
77 métodos
wp.*posts · mídia · comentários, 50
metaWeblog · mt · bloggerantigos protocolos de blog, 23
pingback.*pingbacks, 2
demo.*chamadas de teste para checar o funcionamento, 2
Resultado
Mantém a conexão com o WordPress.com
Verifica usuário e senha no corpo da requisição → cria e edita posts
O servidor abre ele mesmo o endereço de origem para conferir o link
Sem login: Hello! Resposta
Sem processamento
O Jetpack mantém a conexão com o WordPress.com por esse arquivo, então bloquear o arquivo derruba a conexão.
Ferramentas externas de publicação fazem login com o nome de usuário e a senha colocados no corpo da requisição, sem passar pela tela de login.
Um pingback vai para o xmlrpc.php do site linkado no post, e o servidor que recebe abre ele mesmo o endereço de origem para conferir o link.
O demo.sayHello é uma chamada de teste que verifica se o XML-RPC está funcionando; ele responde sem login com uma só palavra, [Hello!].
Se você abrir o endereço no navegador, volta um 405 com a mensagem [XML-RPC server accepts POST requests only.].

2️⃣ Ataques ao xmlrpc.php vistos no log de acesso

A adivinhação de senhas não passa pela tela de login: entra pelo [xmlrpc.php]. No log de acesso de um servidor Nginx + Apache que hospeda vários sites WordPress, das 0h às 20h de 14 de outubro, foram 8.624 requisições POST /xmlrpc.php vindas de 72 IPs, e 19 desses IPs enviaram exatamente 300 cada um.

Um IP enviou 300 requisições em 28 minutos, das 14h09 às 14h37, com um intervalo de 5 a 6 segundos constante até o fim. 8.524 requisições chegaram com Jetpack ou WordPress.com no User-Agent, e o mesmo IP alternava vários valores que imitavam o formato real do Jetpack, como [Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)].

Até as 14h daquele dia, a opção de desativar o XML-RPC do plugin de segurança mandava essas requisições de volta para a home com um 302; depois das 14h, com o bloqueio no Nginx, elas terminaram em 403. O 302 é resultado de o PHP rodar a cada requisição, mesmo com o corpo de 0 byte; o 403 é uma resposta que não passou pelo PHP.

🧭 Um ataque ao xmlrpc.php numa linha do log de acesso
Diagrama explicativo
🛡️ Estado do servidor
Uma linha do log de acesso (Nginx access.log)
203.0.113.25IP[14/Oct/2026:14:09:04 +0900]Hora"POST /xmlrpc.php HTTP/1.1"Solicitação302403Status0153Bytes"Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)"User-Agent
PHP roda · 302 para a homeSem passar pelo PHP · página 403 padrão do Nginx
Com o XML-RPC desativado pelo plugin de segurança, fica registrado um 302 que manda a requisição de volta para a home. O corpo tem 0 byte, mas o PHP roda para gerar esse 302.
Com o bloqueio no Nginx, ficam registrados um 403 e a página de erro padrão de 153 bytes, e o PHP não roda.
Requisições do mesmo IP
14:09:0414:37:20
300 em 28 minutos, a cada 5–6 s
8.624 POST das 0h às 20h72 IPs19 IPs enviaram 300 cada8.524 com UA falso de Jetpack/WordPress.com

3️⃣ system.multicall e adivinhação de senhas

Muitas explicações dizem que o [system.multicall] testa centenas de senhas numa só requisição, mas, quando um login falha uma vez dentro da requisição, o WordPress não compara mais as senhas das chamadas restantes. Só a primeira chamada passa pelo wp_authenticate; as seguintes recebem, sem verificação, o mesmo erro: [Incorrect username or password.].

Como volta uma resposta para cada chamada, uma única requisição com 100 senhas erradas recebe 100 erros numa resposta de 23 KB em 0,12 segundo. Se a senha certa estiver mais para o fim da mesma requisição, ela é recusada junto por causa da falha anterior; se estiver no começo, só essa chamada dá certo. A senha é comparada uma vez, mas a leitura do XML e o carregamento do WordPress se repetem a cada requisição, então a carga que esse ataque gera no servidor vem do número de requisições em si, e não da verificação de login.

4️⃣ 3 formas de desativar o XML-RPC e os códigos de resposta

Mesmo desativando o XML-RPC num plugin, as requisições ao [xmlrpc.php] chegam até o PHP. Com o filtro aplicado, enviei uma requisição com 100 chamadas e ainda assim voltaram HTTP 200 e 100 erros (24 KB). Os métodos se dividem em três, conforme a camada que recusa a requisição, e cada um tem um código de resposta diferente.

1. Desativar por código com o filtro xmlrpc_enabled

Basta colocar no functions.php do tema uma linha de add_filter que faça o filtro [xmlrpc_enabled] devolver false. Assim, as chamadas que exigem login são recusadas com o erro [XML-RPC services are disabled on this site.] e HTTP 405, mas a chamada de teste demo.sayHello, que não exige login, continua respondendo com 200.

2. Desativar nas configurações do plugin: Perfmatters · Hide My WP

É o método de desativar pela opção Disable XML-RPC de plugins como o Perfmatters e o Hide My WP. O Hide My WP usa o mesmo filtro de cima, então as respostas também são iguais, mas o Perfmatters, além do mesmo filtro, encerra com 403 as requisições diretas ao [xmlrpc.php] e ainda remove o cabeçalho e o link de pingback, de modo que, depois de desativado, tanto GET quanto POST respondem 403. Só que, para gerar esse 403, o WordPress roda até a etapa init, então a resposta leva 0,13–0,15 segundo, o mesmo tempo de quando está aberto, e a carga de PHP continua.

🔌 Desativar o XML-RPC nas configurações do plugin
📍 Settings › Perfmatters › General › Core › Disable XML-RPC
Ative a chave e clique em [Save Changes]; a tela do visitante abaixo muda.
Painel de administração
Settings saved.
perfmatters
⚙ GeneralVersion 2.6.8
Core
Disable Emojis?
Disable Dashicons?
Disable Embeds?
Disable XML-RPC?
Remove RSD Link?
Remove jQuery Migrate?
Hide WP Version?
Remove Shortlink?
Disable RSS Feeds?
Remove RSS Feed Links?
Disable Self Pingbacks?
Disable REST API?
Remove REST API Links?
Disable Google Maps?
Disable Password Meter?
Disable Comments?
Remove Comment URLs?
Add Blank Favicon?
Remove Global Styles?
Block Style Behavior?
Disable Heartbeat?
Heartbeat Frequency?
Limit Post Revisions?
Autosave Interval?
Login URL
Custom Login URL?
Disabled Behavior?
Message?
📍 Hide My WP › Settings › Cleanup › Disable XML RPC
Marque a opção e clique em [Save Settings]; a tela do visitante abaixo muda.
Painel de administração
Hide My WP
Settings was updated successfully!
Cleanup
Minify HTML
Remove comments and whitespaces from HTML (Use with cache).
Minify style
Remove theme info and other comments from stylesheet. (Require new style name)(Use with cache!).
Replace \/ URLs
Choose if you see Javascript URLs (e.g. \/wp-content\/themes)
Feed Meta
Other Meta
Default Tagline
Remove Version
Body Classes
Menu Classes
Post Classes
Clean up other
Replace in AJAX
Change Nonce
Disable XML RPC
Internal CSS
Useful to paste internal CSS code (Require New Theme Path)
Internal JS
Useful to paste internal JS code (Require New Theme Path)
Tela do visitante sem login — requisições com as configurações salvas
405
XML-RPC server accepts POST requests only.
Requisições GET recebem 405 em qualquer configuração.
200
Hello!
É uma chamada de teste para checar o funcionamento, por isso responde sem login.
200
faultCode 403 · Incorrect username or password.
A comparação de senha é executada. Trocar a URL de login não muda isso.
403
Sem corpo
O Perfmatters encerra com 403 na etapa init. O PHP roda.
403
Sem corpo
O Perfmatters encerra com 403 na etapa init. O PHP roda.
403
Sem corpo
Termina em 403 sem comparação de senha. O PHP roda.
200
Hello!
Como é pelo filtro, as chamadas que não exigem login continuam respondendo.
405
faultCode 405 · XML-RPC services are disabled on this site.
Só as chamadas que exigem login são recusadas.

3. Bloquear no servidor web: Nginx · Apache

Seja no Nginx ou no Apache, a requisição é recusada antes do PHP, então, ao contrário dos métodos por plugin, o WordPress nem chega a rodar.

# Nginx — dentro do bloco server, acima do location que processa o PHP
location = /xmlrpc.php {
    deny all;
}

Esse bloco location fica acima do location que processa o PHP, e o Nginx recusa na hora as requisições que correspondem exatamente ao [xmlrpc.php]. Depois de incluí-lo, teste a configuração e recarregue para que passe a valer.

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
systemctl reload nginx

A partir da recarga, GET e POST terminam em 403 sem chegar ao PHP; o corpo da resposta é a página 403 padrão do Nginx, de 153 bytes, e a resposta leva 0,02 segundo.

# Apache — .htaccess na raiz do site (Apache 2.4)
<Files xmlrpc.php>
    Require all denied
</Files>

Num servidor só com Apache, basta colocar a diretiva Files acima no .htaccess da raiz do site para valer na hora, e a página 403 padrão do Apache (199 bytes) volta nos mesmos 0,02 segundo.

curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'
403

O comando acima envia a chamada de teste demo.sayHello por POST e confere o código de resposta; depois do bloqueio no servidor web, volta 403. Se você enviar sem as opções [-o /dev/null] e [-w], aparece o corpo: se vier [Hello!], está aberto. Um site desativado só pelo filtro dá a mesma resposta, então é preciso enviar também uma chamada de login para confirmar o 405.

5️⃣ Quando não desativar e as alternativas

Bloquear tudo no servidor web é o método de maior efeito, mas não serve para sites conectados ao WordPress.com pelo Jetpack. O Jetpack depende da conexão XML-RPC e, em hospedagens que bloqueiam o XML-RPC por padrão, como a WP Engine, é preciso abrir uma exceção seguindo as orientações do Jetpack para conectar. O mesmo vale para sites que querem receber pingbacks.

Esses sites restringem o acesso liberando só a lista de IPs que o Jetpack publica (jetpack.com/ips-v4.txt) e recusando o resto. Basta colocar no bloco location do Nginx uma linha allow por faixa de IP e terminar com deny all.

Se não der para bloquear de vez, resta aplicar um limite por minuto às requisições sem login. A configuração é a mesma da seção de limite de taxa do Nginx no artigo Segurança do wp-admin e, como conta só as requisições sem cookie de login, não afeta o trabalho de administração feito com login.

🔢 FAQ & conteúdo recomendado

Mesmo apagando, a próxima atualização do núcleo do WordPress copia de novo o arquivo do pacote, então ele volta na próxima atualização. Em vez de apagar o arquivo, bloquear esse endereço no servidor web ou desativar pelo filtro continua valendo depois das atualizações.

A REST API é uma entrada separada, pelo endereço [wp-json], então continua aberta mesmo com o XML-RPC desativado. Como o editor de blocos usa essa entrada, o certo é não desativá-la por completo e bloquear só as requisições sem login.

Eles não conferem: simplesmente enviam. No WordPress, esse arquivo fica no mesmo caminho em todos os sites, então os scanners só trocam o domínio e mandam POST. O WordPress padrão ainda expõe sozinho o endereço do xmlrpc.php, pelo link [EditURI] no head da página e pelo cabeçalho [X-Pingback] nos posts que recebem pingbacks.

🛡️ Conteúdo recomendado sobre segurança no WordPress 8 artigos · 2 externos
🗂️ Segurança no WordPress
📋 Segurança do wp-admin 📄 9 defesas contra força bruta 📄 Hide My WP 📄 Os 5 melhores plugins de segurança
📄 Desativar o XML-RPC 📄 REST API 📄 Pingbacks · trackbacks 📄 Segurança do wp-config.php
📡 Nanaco Japan: como lidar com ataques ao xmlrpc.php 📡 サイトの主治医: desativar o xmlrpc.php e conferir
Nenhum resultado encontrado.

ℹ️ Aviso de afiliados
O conteúdo deste site contém links de afiliados. Quando um visitante compra um produto ou serviço por meio de um desses links, o site recebe uma comissão do vendedor. O valor que o comprador paga (em promoções, ele diminui ↓) não aumenta por causa disso. Preços, descontos e informações de estoque valem para o momento em que o texto foi escrito e podem ter mudado, por isso confirme com o vendedor antes de comprar. Os produtos são escolhidos e avaliados por critérios próprios, e o recebimento de comissão não influencia a ordem de apresentação nem o conteúdo das avaliações.