Skip to content

[Bug]: Batch indexer writes current HTTP request URL as permalink for all posts in batch #23098

@postjoe

Description

@postjoe

Prerequisites

  • I've read and understood the contribution guidelines.
  • I've searched for any related issues and avoided creating a duplicate issue.

Please give us a description of what happened

Plugin version: Yoast SEO 27.2
WordPress version: 6.9.4
PHP version: 8.3.6
Theme: Salient 18.0.2
Table prefix: non-standard (abc_ instead of wp_)


Summary

When "Index your content" runs from WP Admin → Yoast SEO → Tools, the batch indexer writes the URL of the page currently loaded in the browser as the permalink value in yoast_indexable for every post processed in the batch — instead of each post's actual permalink.


Steps to Reproduce

  1. Have a site with published posts using a custom table prefix.
  2. Navigate to WP Admin → Yoast SEO → Tools → Index your content.
  3. Run the indexer to completion.
  4. Query the indexable table:
SELECT object_id, permalink FROM {prefix}yoast_indexable WHERE object_type='post' LIMIT 20

Expected: Each row's permalink matches that post's actual URL (as returned by get_permalink($object_id)).

Actual: Every post processed in the batch has an identical permalink value — the URL of the admin page (or whatever URL was "current" during the AJAX run).


Reproduction Confirmed Twice, With Different Poison URLs

  • Run 1: Indexer triggered while /affiliate-disclosure/ was the last non-admin page visited. All 28 affected posts received permalink = https://example.com/affiliate-disclosure/.
  • Run 2: Indexer triggered while /about/ was in context. Same 28 posts received permalink = https://example.com/about/.

Both runs were confirmed by querying the yoast_indexable table directly before and after. The canonical column is NULL on all affected rows — meaning the permalink column is what Yoast uses to generate the <link rel="canonical"> tag and all schema @id values.


Impact

  • Every post affected outputs the wrong URL in <link rel="canonical">, <meta property="og:url">, and the entire JSON-LD schema graph (Article @id, WebPage @id, BreadcrumbList @id, ReadAction target).
  • Google is told all affected posts are duplicates of whichever page happened to be "current" when the indexer ran.
  • The canonical column in the indexable is NULL (no user override), so there is no post-level meta to fall back to. The bad permalink value is authoritative.

Workaround: Delete the affected rows from yoast_indexable. Yoast's lazy rebuild on first page request generates the correct permalink. The bug only manifests when the batch indexer runs.


Likely Cause

The batch indexer processes posts via REST API AJAX calls. The permalink resolution for each post appears to be picking up the URL from the HTTP request context (referrer, $_SERVER['REQUEST_URI'], or similar) rather than calling get_permalink($post_id) in a clean per-post context. The result is that all posts in a batch share a single URL — whichever URL was active when the AJAX request was initiated.

Step-by-step reproduction instructions

Reproduction confirmed twice, with different poison URLs

Both runs were confirmed by querying the yoast_indexable table directly before and after. The canonical column
is NULL on all affected rows — meaning the permalink column is what Yoast uses to generate the tag and all schema @id values.

Steps to reproduce

  1. Have a site with published posts using a custom table prefix.
  2. Navigate to WP Admin → Yoast SEO → Tools → Index your content.
  3. Run the indexer to completion.
  4. Query the indexable table: SELECT object_id, permalink FROM {prefix}yoast_indexable WHERE object_type='post'
    LIMIT 20

Expected results

  1. Impact
  • Every post affected outputs the wrong URL in , , and the entire
    JSON-LD schema graph (Article @id, WebPage @id, BreadcrumbList @id, ReadAction target).
  • Google is told all affected posts are duplicates of whichever page happened to be "current" when the indexer
    ran.
  • The canonical column in the indexable is NULL (no user override), so there is no post-level meta to fall back
    to. The bad permalink value is authoritative.

Workaround: Delete the affected rows from yoast_indexable. Yoast's lazy rebuild on first page request generates
the correct permalink. The bug only manifests when the batch indexer runs.

Actual results

Impact

  • Every post affected outputs the wrong URL in , , and the entire
    JSON-LD schema graph (Article @id, WebPage @id, BreadcrumbList @id, ReadAction target).
  • Google is told all affected posts are duplicates of whichever page happened to be "current" when the indexer
    ran.
  • The canonical column in the indexable is NULL (no user override), so there is no post-level meta to fall back
    to. The bad permalink value is authoritative.

Workaround: Delete the affected rows from yoast_indexable. Yoast's lazy rebuild on first page request generates
the correct permalink. The bug only manifests when the batch indexer runs.

Screenshots, screen recording, code snippet

No response

Which editor is affected (or editors)

  • Block Editor
  • Gutenberg Editor
  • Elementor Editor
  • Classic Editor
  • Other (please specify in additional info)

Which browser is affected (or browsers)

  • Chrome
  • Firefox
  • Safari
  • Other (please specify in additional info)

Device you are using

No response

Operating system

No response

PHP version

8.3.6

WordPress version

6.9.4

WordPress Theme

Salient 18.0.2

Yoast SEO version

27.2

Gutenberg plugin version (if relevant)

No response

Elementor plugin version (if relevant)

No response

Classic Editor plugin version (if relevant)

No response

Relevant plugins in case of a bug

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions