The URL That Would Not Show Up in a Search: Where a Five-Year-Old Design Decision Was Hiding
Every time I regenerated my sitemap, a URL that no longer exists came back with it.
This went on for a while. Run the crawler, and there it was again: the path to a product page I had retired four years earlier, picked up neatly, every single time.
It looked like /materia/fortuna15/... , an old hierarchy. No such page exists on the site now.
I looked in the obvious places first
My first suspicion was the article text. Somewhere I had written an old link into an article and forgotten about it. That was the theory.
Nothing there.
Next the menus, the modules, the template settings. All empty.
So as a last resort I searched the whole database. phpMyAdmin can look for a string across every table at once. If nothing turns up there, then at least it is not in the database.
Zero results.
Now I was stuck. Not in the database. Not in an article, not in a menu. And yet the crawler found that URL every time.
It had to be somewhere. I just could not find it the way I was looking.
The reason was a single slash
Here is the answer up front.
The value was stored in the database as JSON. And in JSON, a forward slash is sometimes escaped and written like this: \/
So the string actually sitting in the database was:
materia\/fortuna15\/
What I had been typing into the search box was materia/fortuna15 . No backslashes. So it did not match.
It was there. It simply would not come up in a search.
This is not specific to Joomla. Any CMS that stores custom field values or plugin settings as JSON can do the same thing. Not finding something in a database search does not mean it is not there.
The culprit was me, in 2021
I changed my approach, took a dump of the database and worked through it by hand.
The source was a single article published in March 2021. To drop a list into the individual material pages, I had built a subform-type custom field.
It held links for fifty-three colours, in each of three widths: 6 mm, 15 mm and 21 mm. Three fields, one hundred and fifty-nine entries.
Worse, the same entries existed again in the translation table for the English version of the site. Another hundred and fifty-nine. Three hundred and eighteen links in total, all still pointing at a hierarchy from five years ago.
I have rebuilt the site more than once. I changed the URL structure. What visitors see is the current version.
And still, those absolute paths written inside a field had survived, untouched by any of it.
They appear nowhere on screen, so there was no way to notice them. The only thing that noticed was the crawler.
There was a trap waiting before the replacement
The fix itself is simple. Swap the old path for the new one.
But at that point I noticed something dangerous.
In the English translation table there was a row whose body text contained the ordinary English word material. If I had run a bulk replace on the string materia alone, it would have caught material as well and broken it.
The shorter the string you specify, the more dangerous a bulk replace becomes. Here, the right move was to specify the whole path.
materia\/fortuna15\/ becomes perla\/15mm-width\/
Replace the whole thing, escapes and all. Do that and the English word material is never touched.
I ran the work in three stages. Count the rows with a SELECT before replacing. Run the UPDATE. Then run another SELECT to confirm the old paths are down to zero and the new ones match the expected count.
The result: zero old paths, and fifty-three new ones in each of the three fields. The English side matched, and the word material that nearly got caught came through intact.
Reading it as a design problem
This looks like a maintenance story, but I think it is a design story.
Back in 2021 I wrote the links directly into the field, because at the time that was the quickest way. And it worked.
The problem is that the value then sank somewhere invisible. Text in an article is right there in the editor. A menu item sits in the admin list. But one hundred and fifty-nine links buried in a subform will never meet your eye again unless you open each one and check it.
Where you keep a link is not a question of appearance. It is a question of whether you can fix it later.
Today I would point at the article ID instead, or at the very least keep the reference relative, so that changing the URL structure does not drag the links down with it.
When we rebuild a site, we fix what we can see. What we cannot see, we cannot fix.
So the thing to ask while building is whether this will still be visible later.
The practical summary
- A database-wide search coming back empty does not mean the value is not there
- Values stored as JSON may have their slashes escaped as \/
- When you cannot find something, taking a dump and reading it directly is faster
- The shorter the string in a bulk replace, the more dangerous it is. Specify something long and unique, such as the full path
- Put a SELECT on either side of the replacement and check the counts
It cost me time to hunt for something that would not show up, but once the cause was clear it was nothing at all. I hope it helps anyone stuck in the same place.