Disable XML-RPC
Turns off WordPress's XML-RPC endpoint, closing off a common brute-force and DDoS amplification vector that most sites never actually use.
- Difficulty
- Beginner
- Reading time
- ~1 min
- Where to add it
- A snippet manager plugin (e.g. WPCode, Code Snippets)
- Last updated
- Snippet type
- Filter Snippet
- Tested with
- WordPress 6.7 · PHP 8.3
The Problem
XML-RPC (xmlrpc.php) was built so desktop blogging clients and the WordPress mobile app could publish posts remotely, but very few sites still use it that way. Meanwhile it's one of the most heavily abused endpoints on WordPress: attackers use its `system.multicall` method to test hundreds of username/password combinations in a single request, bypassing normal rate limits, and use `pingback.ping` to turn your site into part of a DDoS amplification attack against someone else's server. WordPress ships XML-RPC enabled by default with no admin toggle, so unless a plugin (like Jetpack or the WordPress app) explicitly needs it, it's exposed attack surface with no upside for most sites.
The Code
add_filter( 'xmlrpc_enabled', '__return_false' );
// Also remove the X-Pingback header and pingback methods specifically.
add_filter( 'wp_headers', function ( $headers ) {
unset( $headers['X-Pingback'] );
return $headers;
} );
add_filter( 'xmlrpc_methods', function ( $methods ) {
unset( $methods['pingback.ping'] );
unset( $methods['pingback.extensions.getPingbacks'] );
return $methods;
} );How It Works
The first line uses the `xmlrpc_enabled` filter, which WordPress core checks before allowing any XML-RPC request to be processed — returning `false` makes every request to xmlrpc.php fail immediately, before any authentication attempt is even parsed. That alone stops the brute-force and amplification abuse. The second block removes the `X-Pingback` HTTP header that WordPress normally sends on every page, which some scanners use to detect that XML-RPC is present at all — hiding the header means fewer automated tools even bother probing further. The third block goes one step further and explicitly deregisters the two pingback-related XML-RPC methods from the method list, which matters if you're using a security plugin or CDN rule that re-enables XML-RPC for other legitimate methods (like Jetpack's own sync calls) — this way pingbacks stay blocked even if the endpoint itself is reopened later.
Where To Add This
A snippet manager plugin (e.g. WPCode, Code Snippets). Because this doesn't touch anything in your theme (it's an application-level security policy, not a presentation change), keeping it in a snippet manager means you can disable it instantly if a specific plugin later needs XML-RPC re-enabled — no need to dig through functions.php under time pressure.
Common Customizations
Return a 403 Forbidden Instead of Failing Silently
The base snippet makes XML-RPC requests fail, but doesn't send a distinct HTTP status for it. Hooking `xmlrpc_call` instead lets you send an explicit 403 response, which shows up more clearly in server logs and monitoring tools than a generic failure.
add_action( 'xmlrpc_call', 'devmubs_block_xmlrpc_with_403' );
function devmubs_block_xmlrpc_with_403() {
status_header( 403 );
wp_die(
'XML-RPC services are disabled on this site.',
'Forbidden',
array( 'response' => 403 )
);
}Block via .htaccess (Apache)
If your site runs on Apache and you have server-config access, blocking at the web-server level rejects the request before PHP or WordPress ever loads — slightly more efficient than the PHP filter under real attack traffic.
<Files xmlrpc.php>
Require all denied
</Files>Block via Nginx Config
The Nginx equivalent of the .htaccess rule above — add this inside your site's `server` block and reload Nginx for it to take effect.
location = /xmlrpc.php {
deny all;
}Allow XML-RPC Only From Trusted IP Addresses
For sites that genuinely need XML-RPC for a specific integration (a self-hosted publishing tool, an internal service), this keeps it reachable only from a known list of IPs instead of disabling it entirely.
add_filter( 'xmlrpc_enabled', 'devmubs_allow_xmlrpc_from_trusted_ips' );
function devmubs_allow_xmlrpc_from_trusted_ips( $enabled ) {
$trusted_ips = array( '203.0.113.10', '203.0.113.11' ); // Replace with your own IPs.
return in_array( $_SERVER['REMOTE_ADDR'], $trusted_ips, true );
}Keep XML-RPC Enabled, Block Only Pingbacks
For sites that still need XML-RPC for remote publishing but want the pingback-based DDoS amplification risk closed off specifically, skip the `xmlrpc_enabled` filter entirely and use just this block.
add_filter( 'wp_headers', function ( $headers ) {
unset( $headers['X-Pingback'] );
return $headers;
} );
add_filter( 'xmlrpc_methods', function ( $methods ) {
unset( $methods['pingback.ping'] );
unset( $methods['pingback.extensions.getPingbacks'] );
return $methods;
} );Log Blocked Attempts Before Disabling
If you want visibility into how often your site is actually being probed before or instead of hard-blocking, log each attempt to the PHP error log first.
add_filter( 'xmlrpc_enabled', 'devmubs_log_and_block_xmlrpc' );
function devmubs_log_and_block_xmlrpc( $enabled ) {
if ( $enabled ) {
error_log( sprintf(
'XML-RPC request blocked from %s at %s',
$_SERVER['REMOTE_ADDR'],
current_time( 'mysql' )
) );
}
return false;
}Common Errors
Jetpack stops syncing or shows a connection error after adding this snippet.
Why it happens: Jetpack's core connection and some of its older sync features rely on XML-RPC being reachable; disabling it wholesale removes that path.
Fix: Skip this snippet if you actively rely on Jetpack's legacy connection, or use the "Allow XML-RPC Only From Trusted IP Addresses" customization above and check Jetpack's current documentation for its connection IP ranges.
A security plugin still reports XML-RPC as "enabled" after adding this snippet.
Why it happens: Some scanners only check that xmlrpc.php returns a response, not whether an actual method call succeeds — the file still exists and responds, it just refuses to process any method once requested.
Fix: Confirm the fix directly: visit /xmlrpc.php in a browser and confirm it shows "XML-RPC services are disabled on this site" instead of the default "accepts POST requests only" message.
The WordPress mobile app can no longer publish posts remotely.
Why it happens: Older versions of the WordPress app used XML-RPC for remote publishing; disabling `xmlrpc_enabled` removes that path entirely.
Fix: Update the app — current versions use the REST API with application passwords and aren't affected. If you must keep using an old app version, don't apply this snippet.
A pingback from another WordPress site stops registering as expected.
Why it happens: This is the intended effect of removing `pingback.ping` — pingbacks are an XML-RPC-based feature by design.
Fix: If you want to keep receiving legitimate pingbacks while still closing the DDoS-amplification risk, don't disable XML-RPC entirely — a dedicated anti-spam plugin is a better fit for that specific balance.
Best Practices
- Apply this through a snippet manager rather than editing wp-config.php or server config directly, so you can verify the change on staging and roll it back with one click if an integration later needs XML-RPC.
- Pair this with login rate limiting (see Limit Login Attempts) — disabling XML-RPC closes one brute-force path, not the standard wp-login.php one.
- Re-check this snippet whenever you add a plugin that advertises remote publishing, mobile sync, or "connect your site" functionality — several legitimate integrations still depend on XML-RPC being reachable.
- Prefer the .htaccess/Nginx alternative over the PHP filter when you have server-config access — it rejects the request before WordPress even bootstraps.
- Don't treat this as a complete security posture on its own — it closes one specific, well-documented attack surface, not a substitute for keeping WordPress core, plugins, and themes updated.
Warnings & Caveats
- If you use the WordPress mobile app or Jetpack's older XML-RPC-based sync, this will break that specific feature — check your plugin list first.
- This does not replace a firewall or login rate limiter — it closes one specific attack surface, not brute-force attempts against wp-login.php itself.
- Always back up your site before adding custom code.
FAQ
What is XML-RPC in WordPress?
Why is XML-RPC considered a security risk?
How do I check if XML-RPC is enabled on my site?
Should I disable XML-RPC on every WordPress site?
Can I disable XML-RPC without editing any code?
When should I use the .htaccess or Nginx method instead of the PHP snippet?
What's the best way to confirm this snippet is actually working?
What's a common mistake people make when disabling XML-RPC?
Does disabling XML-RPC affect the WordPress REST API?
Will this snippet break my site if I add it and don't actually need XML-RPC?
Changelog
- v1.1
Added common customizations (403 response, .htaccess/Nginx alternatives, trusted-IP allowlist, logging), common errors, best practices, and FAQ.
- v1.0
Initial publication.