Opens in a new tab
  1. Home
  2. Guides
  3. Disable
Disable guide

Disable Self-Pingbacks without disabling XML-RPC

Learn how to stop WordPress self-pingbacks by filtering same-site URLs from the pre_ping queue without removing internal links or disabling XML-RPC, including incoming versus outgoing pingbacks, Discussion Settings, XML-RPC method controls, testing and troubleshooting.

  • Updated September 23, 2026
  • 25 min read
  • WordPress guide

Disabling self-pingbacks without disabling XML-RPC lets WordPress keep useful remote communication features while preventing your own internal links from generating unnecessary pingback notifications on the same website.

The important distinction is:

Internal link
→ should remain

Self-pingback
→ can be removed

XML-RPC
→ can remain available

You do not need to disable:

xmlrpc.php

just because WordPress creates pingbacks when one of your posts links to another post on the same site.

WordPress provides a much narrower interception point:

pre_ping

This action fires before WordPress processes the outgoing URLs it intends to ping.

By removing only same-site URLs from that queue, you can keep:

  • internal links;
  • external pingbacks where required;
  • XML-RPC functionality;
  • remote publishing or other XML-RPC integrations;
  • normal post content;

while eliminating redundant:

your site
→ notifying your site
→ that your site linked to your site

This guide explains how WordPress self-pingbacks work, how pre_ping fits into the process, how to disable only self-pingbacks with PHP, why the common xmlrpc_enabled filter does not mean what many tutorials claim, how external and incoming pingbacks remain separate concerns and how TheOneWP Disable Self Pingbacks implements the same targeted approach.

Quick answer

A simple WordPress implementation is:

function project_disable_self_pingbacks(
    &$links
) {

    $home_url =
        home_url( '/' );

    foreach (
        $links as $key => $link
    ) {
        if (
            str_starts_with(
                $link,
                $home_url
            )
        ) {
            unset(
                $links[ $key ]
            );
        }
    }
}

add_action(
    'pre_ping',
    'project_disable_self_pingbacks'
);

The logic is:

WordPress finds URLs in post
↓
prepares outgoing ping list
↓
pre_ping fires
↓
same-site URLs removed
↓
external URLs remain
↓
normal ping processing continues

This does not:

  • remove the internal hyperlinks from your posts;
  • disable xmlrpc.php;
  • disable authenticated XML-RPC methods;
  • disable incoming external pingbacks;
  • disable trackbacks globally;
  • delete existing pingbacks.

What is a WordPress self-pingback?

A self-pingback occurs when one WordPress post links to another URL on the same website and WordPress treats that destination as a pingback target.

For example:

Post A
https://example.com/wordpress-security/

contains:

<a href="https://example.com/wordpress-backups/">
    WordPress backups
</a>

WordPress can identify:

https://example.com/wordpress-backups/

as a link that may support pingbacks.

The site can then effectively contact itself and notify the destination post that:

Post A linked to Post B

The result can appear like a comment

Pingbacks are stored through WordPress’s discussion infrastructure.

The destination post may therefore receive a comment-like entry representing the source URL.

After enough internal linking, the administration area can contain records such as:

Article A
→ pingback from Article B

Article B
→ pingback from Article C

Article C
→ pingback from Article A

The internal link itself is useful

The notification usually is not.

A good internal link can help:

  • readers discover related content;
  • search engines navigate the website;
  • establish relationships between topics;
  • build clear information architecture;
  • distribute internal link equity;
  • reduce dead-end content.

The correct solution is therefore not:

remove internal links
to stop self-pingbacks

It is:

keep internal links
+
filter self-pingback targets

For the wider relationship between these systems, see Pingbacks, Trackbacks & Internal Links.

How WordPress pingbacks work

WordPress Core provides the:

pingback()

function for processing links discovered in post content.

The official pingback() documentation describes it as the function that pings back links found in a post.

The process begins with post content

Conceptually:

post published or updated
↓
WordPress examines post content
↓
URLs extracted
↓
already-pinged URLs considered
↓
eligible destinations prepared
↓
pre_ping
↓
pingback discovery
↓
XML-RPC pingback request
↓
destination processes notification

WordPress tracks URLs it has already pinged

WordPress does not necessarily send the same pingback repeatedly every time the post is processed.

Core retrieves the list of URLs previously pinged through:

get_pung()

and updates that information after successful processing.

Self-pingback filtering happens before remote processing

This is where:

pre_ping

becomes useful.

The official WordPress pingback() source fires:

pre_ping

just before processing the links found in the post.

The ping targets are passed by reference

WordPress invokes the action with the link array by reference.

This means your callback can modify the actual list WordPress is about to process.

Conceptually:

$post_links = [
    'https://example.com/internal-post/',
    'https://external.example/article/',
];

pre_ping
↓
remove internal URL

$post_links = [
    'https://external.example/article/',
];

This is more precise than disabling XML-RPC

If the actual requirement is:

Do not ping my own website

then the most direct layer is:

outgoing ping target list

not:

entire remote communication interface

Method 1: disable self-pingbacks with pre_ping

A straightforward implementation is:

function project_disable_self_pingbacks(
    &$links
) {

    $home_url =
        home_url( '/' );

    foreach (
        $links as $key => $link
    ) {
        if (
            str_starts_with(
                $link,
                $home_url
            )
        ) {
            unset(
                $links[ $key ]
            );
        }
    }
}

add_action(
    'pre_ping',
    'project_disable_self_pingbacks'
);

What this code does

First:

home_url( '/' )

retrieves the public home URL configured for the website.

For example:

https://example.com/

Then each outgoing ping target is compared against that prefix.

If the URL begins with:

https://example.com/

it is removed from the queue.

External destinations remain

Suppose the original queue contains:

https://example.com/internal-guide/

https://wordpress.org/documentation/

https://external-site.example/article/

After the callback:

https://wordpress.org/documentation/

https://external-site.example/article/

remain available for normal pingback processing.

Internal links remain inside the article

The callback does not edit:

post_content

It only changes the temporary array of destinations about to be pinged.

The article still contains:

<a href="https://example.com/internal-guide/">
    Internal guide
</a>

The visitor sees no difference

Navigation remains normal.

Only the automated self-notification process changes.

Why the callback parameter uses &

This matters:

function project_disable_self_pingbacks(
    &$links
)

The ampersand means:

pass by reference

so changes made to:

$links

affect the array WordPress continues processing.

Do not return a replacement array

pre_ping is an:

action

rather than a filter expecting you to return a new value.

This is incorrect:

add_filter(
    'pre_ping',
    function ( $links ) {
        return array();
    }
);

A correct implementation modifies the referenced array.

pre_ping receives more information if you need it

Current WordPress fires pre_ping with:

post links
already-pinged URLs
post ID

A more advanced implementation can therefore accept additional arguments:

function project_filter_ping_targets(
    &$post_links,
    &$pung,
    $post_id
) {
    // Custom processing.
}

add_action(
    'pre_ping',
    'project_filter_ping_targets',
    10,
    3
);

You usually do not need all three arguments

For basic self-pingback removal, the destination URL array is sufficient.

Use the additional context only when the rule genuinely depends on:

  • the source post;
  • previously pinged destinations;
  • post type;
  • custom publishing logic.

A more defensive same-host implementation

Simple prefix matching works well when the site consistently uses one canonical URL.

However, real WordPress installations can encounter variants such as:

https://example.com/

https://www.example.com/

http://example.com/

https://example.com/subdirectory/

If you need host-oriented matching rather than an exact URL prefix, you can compare parsed hosts.

function project_disable_self_pingbacks(
    &$links
) {

    $home_host =
        wp_parse_url(
            home_url( '/' ),
            PHP_URL_HOST
        );

    if ( ! $home_host ) {
        return;
    }

    $home_host =
        strtolower(
            $home_host
        );

    foreach (
        $links as $key => $link
    ) {

        $link_host =
            wp_parse_url(
                $link,
                PHP_URL_HOST
            );

        if ( ! $link_host ) {
            continue;
        }

        if (
            strtolower( $link_host )
            ===
            $home_host
        ) {
            unset(
                $links[ $key ]
            );
        }
    }
}

add_action(
    'pre_ping',
    'project_disable_self_pingbacks'
);

Host matching and prefix matching are not identical

Consider a WordPress installation at:

https://example.com/blog/

A host-only check treats:

https://example.com/store/

as local too.

A prefix comparison against:

https://example.com/blog/

would not.

Choose the definition of “self” deliberately

Your rule may mean:

same exact WordPress home URL

or:

same hostname

or, in more complex environments:

one of several mapped domains

Those are different policies.

Do not treat every subdomain as automatically internal

For example:

www.example.com
shop.example.com
docs.example.com
community.example.com

may represent completely separate applications.

A host comparison that deliberately includes sibling subdomains should be designed explicitly rather than assumed.

How TheOneWP identifies self-pingbacks

The current TheOneWP Disable Self Pingbacks implementation is deliberately narrow.

The module:

hooks into pre_ping
↓
retrieves home_url()
↓
loops through queued URLs
↓
checks str_starts_with()
↓
removes matching URLs

The comparison uses the current home URL

The implementation effectively asks:

Does this queued URL
start with the site's
configured home URL?

If yes:

unset URL

If no:

leave it in queue

TheOneWP does not modify the article

The module does not rewrite:

  • post content;
  • anchor elements;
  • permalinks;
  • canonical URLs;
  • internal-link structure.

TheOneWP does not block external pingback destinations

An external URL that does not begin with the current:

home_url()

remains in WordPress’s outgoing ping queue.

TheOneWP does not disable XML-RPC

The verified module does not register:

  • xmlrpc_enabled filters;
  • xmlrpc_methods filters;
  • server-level blocks on xmlrpc.php;
  • REST API restrictions;
  • remote publishing restrictions.

Its responsibility is only:

same-site outgoing
ping destinations

TheOneWP does not delete existing pingbacks

If the database already contains:

self-pingback comments

enabling the module does not remove them.

The setting controls future outgoing processing.

Existing records require separate cleanup

For historical data, review existing comments and pingback entries separately.

See What Happens to Old Comments When You Disable Them for the general distinction between disabling future activity and deleting existing discussion data.

What is XML-RPC?

WordPress exposes the XML-RPC interface through:

https://example.com/xmlrpc.php

The WordPress XML-RPC server implements several remote methods for compatibility and remote communication.

The official wp_xmlrpc_server documentation describes support for APIs including:

  • WordPress methods;
  • Blogger API compatibility;
  • MetaWeblog API compatibility;
  • Movable Type methods;
  • pingbacks.

Pingback methods are part of the XML-RPC server

Current WordPress registers methods including:

pingback.ping

pingback.extensions.getPingbacks

through its XML-RPC server.

The official wp_xmlrpc_server::__construct() source shows those methods in the server’s method registry.

Incoming and outgoing pingbacks are different sides of the process

When your WordPress site links to another website:

your site
→ outgoing pingback sender

When another website tells your WordPress site that it linked to you:

your site
→ incoming pingback receiver

Self-pingbacks combine both roles

Your site can effectively become:

sender
+
receiver

for the same interaction.

That is why the behavior often feels redundant.

How an outgoing pingback works

At a high level:

WordPress extracts URL
↓
pre_ping runs
↓
destination remains eligible
↓
WordPress discovers
destination pingback endpoint
↓
XML-RPC pingback request sent
↓
remote site validates request
↓
remote site may store pingback

WordPress discovers the remote pingback endpoint

Core provides:

discover_pingback_server_uri()

The official discover_pingback_server_uri() documentation covers the discovery process used by WordPress.

This can involve HTTP requests to the linked resource to find its pingback server URI.

Removing a URL during pre_ping avoids later processing

If the internal destination is removed before discovery:

same-site URL
↓
removed during pre_ping
↓
no pingback discovery
↓
no self-ping request

This is one reason pre_ping is the appropriate layer

The system does not need to continue through the remote-discovery and XML-RPC stages for a destination you already know should not be pinged.

Why not disable XML-RPC completely?

You can disable or restrict XML-RPC for independent security or compatibility reasons.

But that is a much broader decision.

A site may still depend on XML-RPC for:

  • legacy remote publishing;
  • external site-management software;
  • mobile or desktop publishing workflows;
  • specialized plugins;
  • older integrations.

If the only unwanted behavior is:

internal link
→ self-pingback

then shutting down unrelated functionality is unnecessary.

The xmlrpc_enabled filter is widely misunderstood

You will often see:

add_filter(
    'xmlrpc_enabled',
    '__return_false'
);

described as:

Disable XML-RPC completely

That description is inaccurate.

xmlrpc_enabled controls authenticated methods

The official xmlrpc_enabled documentation explicitly states that the filter controls XML-RPC methods that require authentication.

It does not automatically disable:

pingbacks
or
other unauthenticated custom methods

This distinction matters

These are separate controls:

xmlrpc_enabled
→ authenticated XML-RPC methods

xmlrpc_methods
→ methods exposed by XML-RPC server

pre_ping
→ outgoing pingback destination list

ping_status
→ whether a post accepts pings

server block
→ whether xmlrpc.php can be reached

Do not use xmlrpc_enabled to solve self-pingbacks

Even conceptually, it is targeting the wrong problem.

The requirement is:

filter local outgoing
pingback destinations

Use:

pre_ping

What if you want XML-RPC but no incoming pingbacks?

That is another valid configuration.

WordPress exposes:

xmlrpc_methods

which can modify the methods registered by the XML-RPC server.

The official xmlrpc_methods documentation states that the filter can add or remove XML-RPC methods.

Example: remove pingback XML-RPC methods only

function project_remove_xmlrpc_pingbacks(
    $methods
) {

    unset(
        $methods['pingback.ping']
    );

    unset(
        $methods[
            'pingback.extensions.getPingbacks'
        ]
    );

    return $methods;
}

add_filter(
    'xmlrpc_methods',
    'project_remove_xmlrpc_pingbacks'
);

This solves a different problem

That code changes:

incoming XML-RPC
pingback functionality

while leaving other XML-RPC methods registered.

The pre_ping approach changes:

outgoing destinations

You can combine them when that matches the policy

For example:

pre_ping
→ prevent self-pings

xmlrpc_methods
→ reject incoming pingbacks

other XML-RPC methods
→ remain available

But do not add the second control if incoming pingbacks are still required.

Outgoing self-pingback vs incoming external pingback

These scenarios are different.

Scenario A: self-pingback

example.com/post-a/
↓ links to
example.com/post-b/
↓
same site generates notification

Scenario B: external incoming pingback

external.example/article/
↓ links to
example.com/post-b/
↓
external site sends notification

pre_ping affects Scenario A on the sending side

It does not automatically stop:

external.example
→ example.com

from sending an incoming pingback.

This allows selective behavior

You can configure:

Self-pingbacks
→ disabled

External outgoing pingbacks
→ enabled

External incoming pingbacks
→ enabled

XML-RPC
→ enabled

which is exactly why a focused self-pingback control is useful.

WordPress Discussion Settings

WordPress also exposes pingback and trackback controls under:

Settings
→ Discussion

The official Discussion Settings documentation distinguishes outgoing link notifications from incoming notifications.

Attempt to notify linked blogs

The setting:

Attempt to notify any blogs
linked to from the post

controls outgoing ping behavior for the normal publishing workflow.

Turning this off is broader than disabling self-pingbacks

If disabled:

internal pingbacks
→ stop

external outgoing pingbacks
→ also stop

Use it when you want no outgoing pingbacks

If your desired configuration is:

never send pingbacks anywhere

then disabling outgoing link notifications globally may be appropriate.

Use pre_ping when external pingbacks should remain

If your desired configuration is:

internal
→ no ping

external
→ allow normal processing

then filtering the outgoing list is more precise.

Allow link notifications from other blogs

The Discussion setting:

Allow link notifications
from other blogs
(pingbacks and trackbacks)

addresses incoming link notifications for new content.

Incoming and outgoing settings are independent

You can theoretically have:

outgoing pingbacks
→ enabled

incoming pingbacks
→ disabled

or:

outgoing pingbacks
→ disabled

incoming pingbacks
→ enabled

The site-wide setting is primarily a default

WordPress discussion defaults can be overridden at the individual-post level.

Changing the global option does not necessarily retroactively rewrite the stored ping status of every historical article.

See WordPress Per-Post vs. Site-Wide Comment Settings.

Self-pingbacks are not trackbacks

Pingbacks and trackbacks are related legacy link-notification systems, but they operate differently.

The official WordPress Trackbacks and Pingbacks documentation distinguishes them as:

Pingbacks
→ automated

Trackbacks
→ manually initiated legacy mechanism

pre_ping specifically targets pingback processing

It does not globally disable:

trackbacks

or rewrite:

ping_status

If you do not need trackbacks either, review them separately

Do not assume:

Disable Self Pingbacks
=
Disable every legacy
link notification feature

Self-pingbacks and comments are also separate

A pingback may appear in the comments administration interface, but disabling self-pingbacks does not disable normal human comments.

You can configure:

Comments
→ enabled

Self-pingbacks
→ disabled

Complete comment disabling is much broader

If the actual requirement is:

no comments
no pingbacks
no trackbacks
no discussion UI

then self-pingback filtering is too narrow.

See How to Completely Disable Comments in WordPress.

TheOneWP Disable Comments handles the wider discussion-removal use case.

Why self-pingbacks are usually administrative noise

The underlying information is already obvious.

If:

Post A contains a hyperlink to Post B

then WordPress already stores that hyperlink in:

Post A's content

A second comment-like record saying:

Post A linked to Post B

often adds little operational value.

They can clutter moderation workflows

On a site with extensive internal linking, self-pingbacks can mix with:

  • real comments;
  • external pingbacks;
  • trackbacks;
  • spam;
  • product reviews or other comment-based content.

Editorial teams usually care about the link, not the self-notification

An editor wants to know:

Does the internal link
help the reader?

not necessarily:

Did WordPress create
another comment object
because of that link?

Does disabling self-pingbacks improve SEO?

The direct SEO value comes from the internal links themselves.

Disabling self-pingbacks does not remove those links.

Therefore:

internal link
→ remains crawlable

anchor text
→ remains

destination relationship
→ remains

self-pingback record
→ prevented

Do not remove internal links to avoid pingbacks

That would solve a minor notification problem by damaging useful site architecture.

Relative internal URLs are not a complete strategy either

You may encounter advice suggesting that every internal link should be rewritten from:

https://example.com/guide/

to:

/guide/

only to avoid self-pingbacks.

Link-format decisions should be based on your content and deployment architecture, not as a workaround for a notification feature that can be filtered directly.

Use the proper hook instead

pre_ping
→ notification workflow

rather than:

rewrite every internal link
→ content architecture

Does disabling self-pingbacks improve performance?

On an ordinary site, the difference is not a dramatic frontend performance optimization.

However, removing same-site ping targets earlier can avoid unnecessary later processing for those destinations.

The broader path can otherwise involve:

  • pingback endpoint discovery;
  • HTTP requests;
  • XML-RPC communication;
  • destination processing;
  • comment insertion where accepted.

The main reason is cleaner behavior

Think of self-pingback filtering primarily as:

workflow cleanup
+
unnecessary request avoidance

rather than a major speed optimization.

Does disabling self-pingbacks improve security?

It reduces one unnecessary same-site behavior.

It does not secure XML-RPC generally.

It does not prevent:

  • external XML-RPC requests;
  • authenticated XML-RPC access;
  • incoming pingbacks;
  • brute-force attempts;
  • vulnerable plugins;
  • malicious traffic.

Do not confuse narrow cleanup with XML-RPC hardening

If XML-RPC itself is not required, that is a separate security and compatibility decision.

See What Is XML-RPC in WordPress, and Why Disable It?.

For the architectural explanation, see XML-RPC in WordPress, Explained.

When should you keep XML-RPC enabled?

Keep it when a verified integration depends on it.

Possible examples include:

  • legacy publishing clients;
  • remote site-management services;
  • specialized mobile workflows;
  • older integrations that have not migrated to REST;
  • custom software using XML-RPC methods.

Do not preserve XML-RPC merely because WordPress has it

The opposite mistake also exists.

If:

nothing uses XML-RPC

then reviewing whether the endpoint should remain available is reasonable.

The important point is that:

XML-RPC policy

should be decided independently from:

self-pingback policy

How to audit XML-RPC dependencies

Before disabling the entire interface, review:

  • site-management services;
  • mobile publishing;
  • desktop publishing applications;
  • legacy automation;
  • custom integrations;
  • server or WAF logs for legitimate xmlrpc.php traffic.

Do not discover a dependency in production

Test XML-RPC changes on staging where possible.

A forgotten integration is much easier to investigate before somebody notices that an automated publishing workflow quietly stopped three days earlier.

Existing self-pingbacks remain in the database

Filtering:

pre_ping

only affects new processing.

Suppose the database already contains:

27 historical self-pingbacks

After enabling the filter:

27 historical records
→ remain

new self-pingbacks
→ prevented

Do not delete historical records automatically

Some websites may want to retain old discussion history.

Others may want to clean it.

That is a separate data-management decision.

Back up before bulk cleanup

If you decide to remove many pingback records from the database, create a current backup first and identify exactly which comment types and records are affected.

Pingbacks have their own comment type

Modern WordPress distinguishes comment records by type.

When auditing the database or using WordPress APIs, identify:

comment
pingback
trackback

rather than assuming everything inside the comments table is an ordinary human comment.

Testing the self-pingback filter

Use a controlled test instead of changing production behavior and hoping the Comments screen looks quieter next week.

Step 1: prepare two posts

Create:

Post A
Post B

on a staging or controlled test installation.

Step 2: confirm the destination permits pingbacks

If Post B does not accept pingbacks, you cannot distinguish:

self-pingback filter worked

from:

destination rejected all pings anyway

Step 3: add an absolute internal link

Inside Post A:

<a href="https://example.com/post-b/">
    Post B
</a>

Step 4: publish or update Post A

Allow the normal WordPress ping workflow to run.

Step 5: inspect Post B

With self-pingback filtering active:

internal link
→ works

new self-pingback
→ absent

Test an external URL too

If the requirement is specifically:

disable only self-pings

test an external pingback-capable destination separately.

You want to verify:

internal URL
→ removed

external URL
→ remains eligible

Testing external pingbacks can be difficult

The remote site must:

  • support pingbacks;
  • advertise or expose the required endpoint;
  • accept the ping;
  • not block your source;
  • permit pings for the target content.

A failed external ping does not automatically mean your pre_ping code removed it.

Log the queue during development if necessary

On a controlled development environment, you can temporarily inspect the targets reaching pre_ping.

function project_debug_ping_targets(
    &$links
) {

    error_log(
        print_r(
            $links,
            true
        )
    );
}

add_action(
    'pre_ping',
    'project_debug_ping_targets',
    5
);

Do not leave noisy debug logging enabled unnecessarily

Production logs can accumulate URLs and unnecessary debugging information.

Remove diagnostic code after testing.

Troubleshooting: self-pingbacks still appear

If a self-pingback is still created, start by comparing the exact linked URL with:

home_url()

Scheme mismatch

Your configured home URL might be:

https://example.com

while old content links to:

http://example.com/post/

A literal prefix comparison can treat those as different.

www mismatch

home_url()
→ https://example.com

post content
→ https://www.example.com/post/

Again, exact prefix matching sees different strings.

Domain aliases

The site may respond to:

example.com

example.net

legacy.example.com

while only one is configured as the canonical WordPress home URL.

Multilingual domains

A multilingual system may use:

example.com

example.fr

example.de

A simple single-prefix rule does not automatically classify every mapped domain as local.

Reverse proxies and host rewriting

Some environments expose one public hostname while WordPress receives or stores another internal hostname.

Review:

  • WordPress Address;
  • Site Address;
  • proxy configuration;
  • canonical redirects;
  • stored post URLs.

Subdirectory installations

Suppose:

home_url()
→ https://example.com/blog

A prefix-based implementation treats URLs beginning with that exact location as local.

A same-host implementation may classify the entire:

example.com

hostname as local instead.

Choose the rule that matches the architecture.

WordPress Multisite requires deliberate testing

Consider:

site-a.example.com
site-b.example.com

Should a link from Site A to Site B count as:

self-ping

or:

external network-site ping

There is no universal answer.

It depends on your desired network behavior.

TheOneWP uses each site’s current home URL

Its exact-prefix strategy naturally keeps the scope narrow.

A separate hostname or network site will not automatically be removed unless its URL begins with the same configured home value.

Troubleshooting: existing records keep appearing

Make sure you are looking at:

newly created pingbacks

rather than historical entries that existed before the setting was enabled.

Check timestamps

Filtering future pings does not erase yesterday’s records.

Troubleshooting: pingbacks were already queued

Publishing activity and scheduled ping processing can mean a request was prepared before your code change.

When testing, use newly created or newly updated controlled content after the filter is active.

Troubleshooting: another plugin sends its own notifications

Not every notification involving links necessarily comes through WordPress Core’s:

pre_ping

workflow.

A plugin could implement:

  • custom HTTP callbacks;
  • webhooks;
  • link tracking;
  • content notifications;
  • proprietary ping systems.

Inspect the actual comment type and source when behavior does not match Core.

Troubleshooting: no pingbacks are sent anywhere

If external pingbacks are also missing, review:

  • Discussion Settings;
  • post-level ping settings;
  • other plugins;
  • XML-RPC restrictions;
  • server outbound HTTP restrictions;
  • destination pingback support.

Do not assume pre_ping is responsible

A targeted self-pingback filter should remove only the URLs matching its local-site rule.

Should you disable all outgoing pingbacks instead?

If the website does not use pingbacks at all, filtering only internal targets may be unnecessarily specific.

You may instead decide:

outgoing pingbacks
→ disabled entirely

through the appropriate discussion configuration.

Use the narrowest control that matches the requirement

A useful decision tree is:

Need external outgoing pingbacks?
│
├─ Yes
│  ↓
│  Disable only self-pingbacks
│  with pre_ping
│
└─ No
   ↓
   Disable outgoing pingbacks
   more broadly

Should you disable incoming pingbacks too?

Ask separately:

Do we want other sites
to notify this site
when they link here?

If no, review:

  • post-level ping status;
  • Discussion Settings;
  • XML-RPC pingback methods.

Do not let one requirement silently answer another

These are independent questions:

Send external pingbacks?
Receive external pingbacks?
Send self-pingbacks?
Allow XML-RPC publishing?
Allow other XML-RPC methods?

A practical configuration matrix

Requirement Appropriate control
Stop only self-pingbacks pre_ping
Stop all outgoing pingbacks Discussion configuration / outgoing ping controls
Stop incoming pingbacks on posts Ping status / Discussion configuration
Remove XML-RPC pingback methods xmlrpc_methods
Disable authenticated XML-RPC methods xmlrpc_enabled
Block xmlrpc.php completely Separate application/server policy
Disable comments completely Broader discussion-system control

How to enable Disable Self Pingbacks in TheOneWP

The workflow is intentionally simple:

1. Open TheOneWP.

2. Open Disable Components.

3. Find Disable Self Pingbacks.

4. Enable the module.

5. Save the settings.

6. Publish or update a controlled
   test post containing a full
   internal URL.

7. Verify that the internal link
   still works.

8. Confirm that no new
   self-pingback is created.

What happens internally

When the option is enabled, TheOneWP loads:

TOWP_Disable_Self_Pingbacks

The class registers its callback on:

pre_ping

and filters the link array before WordPress continues pingback processing.

The implementation has no frontend assets

The module does not need:

  • JavaScript;
  • frontend CSS;
  • REST routes;
  • AJAX requests during normal ping processing;
  • cron jobs;
  • database tables.

Its runtime responsibility is deliberately narrow.

There is no visitor-facing change

A visitor still sees:

normal article
+
normal internal links

The difference exists in WordPress’s background publishing workflow

before
→ internal URL stays in ping queue

after
→ internal URL removed before ping

Known limitation of exact-prefix matching

The current TheOneWP module compares each URL against the exact value returned by:

home_url()

using:

str_starts_with()

This is predictable and efficient for conventional single-domain installations.

However, alternate representations such as:

http vs https

www vs non-www

mapped domains

language domains

proxy aliases

are not automatically normalized into one identity by that comparison.

Use canonical URLs consistently

A well-maintained WordPress site should generally avoid publishing multiple host or scheme variations for the same content anyway.

For migrations, see How to Migrate WordPress URLs Safely.

For canonical URL behavior, see WordPress Canonical URLs, Explained.

Where should a custom self-pingback snippet live?

Appropriate locations include:

  • a site-specific plugin;
  • a must-use plugin;
  • a maintained snippet manager;
  • a child theme when the behavior is intentionally tied to that theme.

A site-specific plugin is often the cleanest architecture

Ask:

Should changing the
frontend theme suddenly
restore self-pingbacks?

If no, the behavior probably belongs outside the theme.

Do not modify WordPress Core

Never edit Core’s:

pingback()

implementation just to remove local URLs.

The:

pre_ping

hook exists precisely so this behavior can be changed without modifying Core.

Do not edit xmlrpc.php

Deleting or modifying:

xmlrpc.php

is not a sensible solution to a self-pingback problem.

Common mistake: disabling XML-RPC just to stop self-pings

If XML-RPC is still needed elsewhere, this is unnecessarily broad.

Common mistake: using xmlrpc_enabled and assuming all XML-RPC disappeared

The official documentation explicitly states otherwise.

Common mistake: removing internal links

The link is useful. The self-notification is the part being filtered.

Common mistake: replacing absolute URLs solely to stop pingbacks

Fix the ping workflow rather than redesigning the content architecture around it.

Common mistake: disabling all outgoing pings when external pingbacks are still required

Use pre_ping for selective destination filtering.

Common mistake: assuming pre_ping blocks incoming pingbacks

It modifies your outgoing ping list.

Common mistake: assuming self-pingback disabling blocks trackbacks

Trackbacks are a separate mechanism.

Common mistake: assuming it disables comments

Human comments remain a separate discussion feature.

Common mistake: expecting old self-pingbacks to disappear

Filtering future activity does not delete historical records.

Common mistake: bulk deleting comments without checking comment type

A WordPress site’s comment table can contain more than ordinary comments.

Common mistake: using a naive hostname rule on Multisite

Decide whether other network sites count as internal or external first.

Common mistake: forgetting www and HTTPS variants

A literal prefix comparison can miss aliases.

Common mistake: forgetting mapped domains

Canonical-domain consistency matters for same-site detection.

Common mistake: testing with an old pingback

Use newly published or updated test content after the filter is active.

Common mistake: blaming the filter when an external site rejects the ping

External destinations control whether they accept pingbacks.

Common mistake: treating pingbacks as an SEO backlink strategy

The useful SEO asset is the actual editorial hyperlink.

A pingback record on another site’s comments system is controlled by that site and should not be treated as a substitute for genuine links or content promotion.

Common mistake: treating self-pingback removal as security hardening for all XML-RPC

It is a focused publishing-workflow change.

Common mistake: blocking xmlrpc.php at the server without checking dependencies

That changes considerably more than self-pingback behavior.

Common mistake: leaving several overlapping snippets active

You may accidentally have:

pre_ping filter
+
Discussion Settings disabled
+
xmlrpc_methods filter
+
server XML-RPC block
+
security plugin controls

When troubleshooting, identify which layer actually owns the policy.

Disable Self-Pingbacks checklist

  • Confirm the problem is specifically self-pingbacks.
  • Do not remove useful internal links.
  • Understand that pingbacks and internal links are separate layers.
  • Understand that pingbacks and trackbacks are different mechanisms.
  • Understand that comments and pingbacks can be configured separately.
  • Review Settings → Discussion.
  • Check whether outgoing pingbacks are still required.
  • Check whether incoming pingbacks are still required.
  • Check whether XML-RPC is required by any integration.
  • Do not assume xmlrpc_enabled disables all XML-RPC methods.
  • Use pre_ping when only outgoing same-site destinations should be removed.
  • Modify the ping target array by reference.
  • Do not expect a return value from the pre_ping action.
  • Retrieve the site’s canonical home URL with home_url().
  • Decide whether exact-prefix or hostname matching fits the architecture.
  • Review HTTP vs HTTPS consistency.
  • Review www vs non-www consistency.
  • Review mapped domains.
  • Review multilingual domains.
  • Review reverse-proxy aliases.
  • Review Multisite behavior.
  • Keep external pingback destinations when required.
  • Remember that incoming pingbacks are separate.
  • Use xmlrpc_methods only when XML-RPC method-level control is actually required.
  • Do not edit xmlrpc.php.
  • Do not modify WordPress Core.
  • Store the customization in a maintainable site-level location.
  • Test with two controlled posts.
  • Use a full internal URL during the test.
  • Verify the internal hyperlink still works.
  • Verify no new self-pingback appears.
  • Test an external destination if external pingbacks should remain.
  • Do not confuse external rejection with local filtering.
  • Review historical pingbacks separately.
  • Back up before bulk deletion.
  • Document the final pingback policy.
  • Document the XML-RPC policy separately.

Related guides

Final recommendation

If the only unwanted WordPress behavior is:

Post A links to Post B
on the same website

↓

WordPress creates a
self-pingback

do not disable XML-RPC merely to stop that notification.

Use the narrowest WordPress control that matches the problem:

pre_ping

and remove only the same-site URLs from the outgoing ping queue.

The resulting architecture is:

Internal links
→ preserved

External outgoing pingbacks
→ preserved when required

Incoming pingbacks
→ separate policy

Trackbacks
→ separate policy

Comments
→ separate policy

XML-RPC
→ remains available

This keeps content architecture independent from notification behavior and keeps XML-RPC policy independent from self-pingback policy.

If you later determine that XML-RPC itself is unnecessary, review and disable it as a separate compatibility and security decision. If incoming pingbacks are unwanted, remove or restrict their XML-RPC methods separately. If all discussion functionality is unnecessary, use a broader comments and pings strategy.

TheOneWP Disable Self Pingbacks implements the focused case. The current module hooks into WordPress’s native pre_ping stage, compares each queued destination against the current home_url() value and removes matching same-site URLs before WordPress continues pingback processing.

It does not rewrite post content, remove internal hyperlinks, block external destinations, delete historical pingbacks or disable XML-RPC.

The useful rule is simple:

Need internal links
but not self-pingbacks?
→ filter pre_ping

Need no outgoing pingbacks?
→ disable outgoing pings

Need no incoming pingbacks?
→ control incoming pings

Need no XML-RPC?
→ handle XML-RPC separately

That separation produces a configuration that is easier to understand, easier to test and considerably less likely to break unrelated WordPress integrations.

Simplify your WordPress stack

A modular WordPress toolkit. 104 focused tools.

Ultimately, you can build cleaner workflows, maintain fewer plugins and enable only the features each website actually needs.