Checking a Redirect Fix a Week Later, With Real Search Console Numbers

On August 29 I pulled a Search Console export and found 30 pages on this site indexed twice. Every one of them had a working clean URL and a second copy at /index.php/ plus the same path, splitting impressions between the two as if they were different pages. I wrote a fix the same morning. This is what a week of real data since then shows, not what I assumed it would show.

What was actually broken

Drupal serves /index.php/foo and /foo as identical content. That's the front controller doing exactly what a front controller does, not a misconfiguration anywhere I control — I checked the nginx vhost first, since that felt like the obvious place to look, and it's Puppet-managed and had nothing path-related to blame. Somewhere, at some point, something linked to or crawled the /index.php/ form of a URL on this site, and once Google has a URL it keeps checking it, so 30 pages had accumulated a shadow copy each. The export showed 566 impressions spread across those shadow copies, and on at least four of them, real clicks were landing on the wrong URL entirely.

The fix, and why it's not the redirect module

drupal/redirect, already installed on this site for retiring duplicate article URLs, only matches exact source paths — no wildcards. Thirty individual redirect rows, one per affected page, growing by one every time a crawler found a new duplicate, wasn't a fix, it was a chore with no end date. What closes the class of problem is a kernel.request event subscriber that runs before Drupal does any routing: it checks whether the request URI matches ^/index\.php/(.+)$, and if it does, issues a 301 to the path with that prefix stripped, at listener priority 100 so it fires ahead of normal routing. Drupal core's own existing bare /index.php/ redirect stays untouched, since the regex only matches when something follows the trailing slash.

What a week actually looks like

I pulled a second export on September 6, same 28-day trailing window, eight days further along. Overall traffic moved in the right direction on its own — 69 to 79 clicks, 4,187 to 4,562 impressions — mostly newer articles entering the index for the first time, unrelated to this fix.

The /index.php/ duplicates: still 29 pages, barely down from 30. But the impressions attached to them dropped from 566 to 447, a fifth gone in eight days. Three specific URLs disappeared from the index entirely in that window. That's Google acting on the redirect, at whatever pace Google recrawls a low-traffic personal site — not instantly, and not on a schedule I get to see.

It's also not a clean sweep. Two /index.php/ URLs that weren't in the August 29 export showed up in the September 6 one, one of them a page whose duplicate hadn't previously carried a query string and now does. The redirect fires the instant one of those requests hits the server, so it isn't that the fix stopped working — it's that Google can apparently still discover a stale link to the old form (a cached sitemap entry, an old internal link, a backlink somewhere I don't control) days or weeks after I shipped the fix, index it once, get redirected on the next crawl, and drop it again on its own timeline. Fixing the mechanism doesn't erase whatever already pointed at the broken form.

The one page with a clean before-and-after

how-run-aider-ai-chat-docker-linux is the clearest single example, because both URLs for it show up in both exports with enough volume to compare directly. The clean URL: position 7.01 to 6.57, impressions 81 to 100, holding at 2 clicks both times. Its /index.php/ duplicate, over the same eight days: 1 click and 46 impressions at position 11.74, down to 0 clicks and 21 impressions at position 17.38. Fewer impressions, a worse position, the one click it had gone. That's the redirect doing its job on one page I can point to and check, not an aggregate I have to trust.

A different problem the same data turned up

The same export flagged something the redirect has nothing to do with: the query silktide consent manager sits at position 7 with real impression volume — 189 in the first export, 175 in the second — and zero clicks in both. Stable at zero across eight days rules out a fluke; something about how that result presents in search (the title, the snippet, a mismatch with what someone searching that phrase expects to find) is turning impressions away before a click ever happens. Different failure mode than duplicate URLs, same lesson underneath: a decent ranking position tells you a page is being surfaced, not that it's being read, and the two numbers can move in opposite directions without anything alerting you to it.

Why the second export mattered more than the fix

Shipping the redirect and confirming it returns a live 301 would have felt like closing the loop. It wasn't the loop. The actual mechanism only shows up by measuring twice: once to find the problem, once again a week later to see how much of it Google had acted on, which pages moved and which didn't, and that two new stale URLs had shown up in the meantime regardless. A fix that's live and a fix that's finished are different claims, and the gap between them here is measured in a slow trickle of index entries disappearing, not a switch flipping.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Please share this article on your favorite website or platform.