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_enabledfilters;xmlrpc_methodsfilters;- 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.phptraffic.
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_enableddisables all XML-RPC methods. - Use
pre_pingwhen only outgoing same-site destinations should be removed. - Modify the ping target array by reference.
- Do not expect a return value from the
pre_pingaction. - 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_methodsonly 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
- WordPress Self-Pingbacks Explained
- Pingbacks, Trackbacks & Internal Links
- XML-RPC in WordPress, Explained
- What Is XML-RPC in WordPress, and Why Disable It?
- WordPress Per-Post vs. Site-Wide Comment Settings
- How to Completely Disable Comments in WordPress
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.

