Skip to content

Pings/Trackbacks: Auto-approve pingbacks from the same site - #12870

Closed
youknowriad wants to merge 1 commit into
WordPress:trunkfrom
youknowriad:claude/self-pingbacks-auto-approve-047368
Closed

Pings/Trackbacks: Auto-approve pingbacks from the same site#12870
youknowriad wants to merge 1 commit into
WordPress:trunkfrom
youknowriad:claude/self-pingbacks-auto-approve-047368

Conversation

@youknowriad

@youknowriad youknowriad commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Pingbacks carry no email address, so they can never satisfy the comment_previously_approved option in check_comment() — they always fall through to return false. That option is on by default, so every pingback is held for moderation indefinitely, including the ones a site sends to itself when a new post links to an older one. The only existing workaround is to disable the option, which also auto-approves pingbacks from every other site.

Trac ticket: https://core.trac.wordpress.org/ticket/65016

Changes

check_comment() now approves a pingback whose source URL resolves to a published post on this site. No new function and no new setting.

The decision is exposed as auto_approve_pingback, defaulting to true for pingbacks from this site and false for all others:

apply_filters( 'auto_approve_pingback', $approve_pingback, $source_id, $url );

Two things worth checking in review:

  • Host matching is delegated to url_to_postid(), which compares parsed hostnames, so a URL that merely contains the home URL is not treated as local.
  • The check sits after the existing gates, so manual moderation, moderation keywords, and the max link count still take precedence.

Why pingbacks only

An earlier revision of this PR also covered trackbacks. That was wrong, and it is worth stating why so the exclusion is not "fixed" later.

A pingback is verified before it is stored: pingback_ping() fetches the source page, requires it to contain a link to the target, rejects source == target, and builds the comment from the fetched page. A trackback has none of that. wp-trackback.php takes the source URL, title, excerpt, and blog name straight from $_POST and never contacts the source.

So trusting a trackback's claimed source means anyone can POST arbitrary content naming a local post and have it approved:

curl -s "https://example.com/2026/08/a-post/trackback/" \
  --data-urlencode "url=https://example.com/2026/08/a-post/#x1" \
  --data-urlencode "title=Cheap X" \
  --data-urlencode "excerpt=BUY https://spam.tld" \
  --data-urlencode "blog_name=ForgedSite"

The trackback dedup is an exact string match on comment_author_url, while url_to_postid() strips the fragment, so #x1, #x2, … all resolve to the same post and all pass. wp_check_comment_flood() throttles this to one per 15 seconds per IP, which is a rate limit rather than an authorization control. Verified locally: unauthenticated, approved on arrival, and held again once trackbacks were excluded. There is a regression test covering it.

Testing

Verified end to end on a real site: publishing a post that links to an older one produces a pingback that lands approved, with an empty moderation queue. Adding add_filter( 'auto_approve_pingback', '__return_false' ) puts the same pingback back at comment_approved = '0', and the forged trackback above is held.

Three things unrelated to this patch get in the way of reproducing that in the standard Docker environment, in case anyone else hits them:

  • Pings are disabled outside production as of 7.1, so wp_should_disable_pings_for_environment needs filtering to false.
  • wp_extract_urls() requires a literal . in the URL, so nothing is extracted with the default localhost:8889 site URL. Using 127.0.0.1:8889 works.
  • The site pings itself over HTTP, and localhost:8889 from inside the php container is that container. Requests need routing to the nginx service, keeping the original Host header so WordPress does not issue a canonical redirect.

Pingbacks are dispatched from a do_pings cron event, so a manual wp cron event run do_pings avoids waiting for a subsequent request.

npm run test:php -- --group comment    # 610 tests, 1498 assertions
npm run test:php -- --group xmlrpc     # 318 tests, 1246 assertions

Open question

The ticket asks whether this should be a setting or automatic; this is the automatic version. Related: #24241, where the opposite preference is argued, and which also asks for trackbacks — see above for why they are excluded.

On multi-author sites, any published post can trigger an auto-approved self-pingback. Restricting this to source posts whose author is the target's author or can moderate_comments would close that, reusing the trust rule already in wp_check_comment_data(). Left out for now — happy to add it if preferred over leaving it to the filter.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Research, implementation, and tests; reviewed and edited by me.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props youknowriad.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment thread src/wp-includes/comment.php Outdated
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@youknowriad
youknowriad force-pushed the claude/self-pingbacks-auto-approve-047368 branch 3 times, most recently from 88555a2 to e8a046f Compare August 6, 2026 09:08
Pingbacks carry no email address, so they never satisfy the
`comment_previously_approved` option, which is enabled by default. Every
pingback is held for moderation indefinitely as a result, including the ones a
site sends to itself when a new post links to an older one.

Approve a pingback whose source URL resolves to a published post on this site.
A pingback is verified before it is stored: the source page is fetched, it must
link to the target, and the comment is built from that page. Trackbacks are
excluded, as their source, title, and excerpt are unverified request data.
Existing moderation checks still take precedence, and the new
`auto_approve_pingback` filter controls the decision.

Props annezazu, avcascade, chriscct7, desrosj, dshanske, eurello, matt,
mohkatz, SergeyBiryukov, stevegrunwell.
Fixes #65016. See #24241.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@youknowriad youknowriad changed the title Pings/Trackbacks: Auto-approve pingbacks and trackbacks from the same site Pings/Trackbacks: Auto-approve pingbacks from the same site Aug 6, 2026
@youknowriad
youknowriad force-pushed the claude/self-pingbacks-auto-approve-047368 branch from e8a046f to 5efe36f Compare August 6, 2026 09:28
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 63036
GitHub commit: b21fdb7

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions Bot closed this Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant