Skip to content

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

php
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.

php
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.

apache
<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.

nginx
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.

php
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.

php
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.

php
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?
XML-RPC is a remote procedure call protocol WordPress has supported since version 3.5, enabled by default, originally built so external applications — desktop blogging clients, the WordPress mobile app, and services like Jetpack — could publish posts and manage comments without using the browser-based wp-admin dashboard. It works through a single endpoint, xmlrpc.php, which accepts XML-formatted requests describing which method to call and with what data.
Why is XML-RPC considered a security risk?
Its system.multicall method lets a single HTTP request bundle hundreds of separate method calls, including login attempts, letting attackers test huge numbers of username/password combinations far faster than submitting them one at a time through wp-login.php. Its pingback.ping method has also been used to make WordPress sites unknowingly participate in DDoS amplification attacks against third parties.
How do I check if XML-RPC is enabled on my site?
Visit yoursite.com/xmlrpc.php directly in a browser. If it's enabled, you'll see "XML-RPC server accepts POST requests only." If you've disabled it with the xmlrpc_enabled filter, you'll see "XML-RPC services are disabled on this site" instead, since WordPress core prints that message when the filter returns false.
Should I disable XML-RPC on every WordPress site?
For most sites, yes — very few actually rely on it once you account for modern WordPress using the REST API instead. The exception is sites actively using a plugin or integration that specifically requires it; check your plugin list before disabling it wholesale, or use the trusted-IP customization above instead of a full block.
Can I disable XML-RPC without editing any code?
Yes — most WordPress security plugins (Wordfence, Sucuri, iThemes Security, and others) include a one-click XML-RPC toggle. This snippet exists for sites that want the same result without adding a full security plugin just for this one setting.
When should I use the .htaccess or Nginx method instead of the PHP snippet?
If your site runs on Apache or Nginx and you have server-config access, blocking at the web-server level rejects the request before PHP or WordPress ever loads. Use the PHP snippet when you don't have server-config access — most managed WordPress hosts and shared hosting plans only give you that level of control.
What's the best way to confirm this snippet is actually working?
Visit /xmlrpc.php directly — you should see "XML-RPC services are disabled on this site" instead of the default "accepts POST requests only" message. For a more thorough check, send a real XML-RPC request (for example, a system.listMethods call via curl) and confirm it's rejected rather than just checking the plain response.
What's a common mistake people make when disabling XML-RPC?
Disabling it wholesale without checking whether Jetpack, a mobile publishing workflow, or another integration actually depends on it — then spending time debugging what looks like a broken feature but is actually this snippet working exactly as intended. Always check your active plugins first.
Does disabling XML-RPC affect the WordPress REST API?
No — they're separate systems. The REST API (used by the block editor, headless WordPress setups, and modern mobile app versions) is unaffected by the xmlrpc_enabled filter entirely; this snippet only touches the legacy XML-RPC endpoint.
Will this snippet break my site if I add it and don't actually need XML-RPC?
No — if nothing on your site uses XML-RPC, disabling it has zero visible effect on normal site behavior. The only way it "breaks" something is if a specific integration you're using genuinely depends on that endpoint, which is why checking your plugin list first is the one real precaution.

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.

We use cookies

We use essential cookies to run this site, and optional cookies to understand usage and remember your preferences. Read our Cookie Policy to learn more.