The bug that turns one page’s rule into every page’s rule

A condition that describes the site, used where a condition about the page was needed. It is quiet, it passes review, and it can remove a whole site from Google.

Here is a category of bug that is quiet, easy to write, hard to see in review, and capable of removing an entire website from Google. I have been thinking about it for a while and I have not seen it written up anywhere, so here it is.

It happens when a condition that describes the site gets used as though it describes the page.

The shape of it

Say you have an archive page that should not be in the index while it is empty. Reasonable goal. There is nothing on it, so there is no reason for Google to hold a copy.

So you write a helper that answers the question:

function docs_archive_is_empty() { return published_doc_count() === 0; }

And you use it where your template decides what the robots tag says:

} elseif ( docs_archive_is_empty() ) { echo ‘<meta name=”robots” content=”noindex, follow”>’; }

Read that again. The helper asks “are there any published docs on this site”. That question has the same answer on every page of the site. It is true on the archive, and it is equally true on the homepage, the service pages, the contact page and every article.

The branch has no idea which page it is on. So if nothing has ever been published into that archive, every page that reaches this branch gets noindex.

Why it survives review

Because the helper is correctly named, correctly written, and does exactly what its name says. There is no bug inside `docs_archive_is_empty()`. It answers its question accurately every time it is called.

The bug is in the gap between the question the helper answers and the question the branch needed answered. The branch needed “am I on the docs archive, and is it empty”. It asked “is the docs archive empty”. One clause short.

And in a review, your eye checks whether the helper is right. It is.

Why tests miss it

This is the part I find genuinely interesting.

If you write tests around a change like this, you will almost certainly write them about the feature. Does the helper exist. Does the template call it. Does it return true when nothing is published. Does it return false when something is. Does the branch sit above the fallback so it can actually fire.

Every one of those passes. All of them are testing that the feature works.

Not one of them asks the question that matters: does this fire anywhere it should not? That test looks different. It renders a page that has nothing to do with the feature, a service page, an article, the homepage, and asserts the feature did not touch it.

The instinct when you build something is to prove it works. The instinct that catches this class of bug is to prove it stays in its lane. Those are different tests and most people only write the first kind.

The general rule

Any condition that changes what a crawler sees has to narrow to a page.

In WordPress that means the condition contains `is_page()`, `is_singular()`, `is_post_type_archive()`, `is_tax()`, or one of your own helpers that takes a post ID and answers about that post. Something that reads a global option, counts rows in a table, or checks a setting is answering a question about the site, and a site-wide answer applied to a page-level decision is how a whole site disappears.

Same applies outside WordPress. Any framework, any language. If the predicate does not take the current request as an input, it cannot be making a decision about the current request.

The check worth automating

If you can, make your build fail on it.

Find every place your code emits `noindex`, and assert that the condition guarding it mentions a page-scoped test. It is a crude check. It is also the difference between finding this in a build log and finding it in a traffic graph three weeks later.

Allow yourself exactly one documented exception, which is the site-wide “discourage search engines” switch, because that one is genuinely supposed to apply everywhere.

The symptom to watch for

If this ever happens to you, the way it shows up is counterintuitive and it is worth knowing in advance.

Your impressions fall. Your clicks fall. And your average position improves.

That last one looks like good news and it is the opposite. As pages drop out of the index, the ones that remain are the pages that were ranking best. The average rises because the bottom of the distribution is being deleted. A rising average position next to falling impressions is not a mixed signal, it is a symptom, and it is the one most likely to talk you out of investigating.

The report that tells you the truth is Indexing, then Pages, in Search Console. If something has gone wrong at this level it will be sitting there in plain language, counted, under a heading that names the cause.

Written by Sean Lee, Palm Projects

I build and rank websites for small businesses. If something here applies to your site and you want a second opinion on it, send it over.

Free tool

Something that does this for you

Runs in your browser. Nothing is sent anywhere and there is no email gate.

Start here

Tell me what you are trying to fix

Send over the site you have now, or the one you wish you had. I will tell you honestly whether I am the right person for it.

info@palmprojects.com