← All posts

What a migration leaves behind

Moving a WordPress site to a new host is a solved problem. Every provider has a migration tool, and they mostly work. What they do not do is tell you what came across that should not have.

I moved six of my own sites earlier this year. I have now audited them three times, and each pass found things the one before it was not looking for. That is the part worth writing down — not any single finding, but that one cleanup was never going to be enough.

The first pass

The obvious debris, and there was plenty:

  • 139 orphaned options from the previous host’s control panel
  • 25 rows of block telemetry nobody had ever read
  • 5 scheduled cron jobs pointing at code that no longer existed

And the one that mattered: a commercial Redis object cache, installed by the old host, still running as a must-use plugin. It loaded on every request and tried to reach a Redis server that does not exist on the new host. Every page view failed a cache lookup, then carried on.

Nothing broke, which is precisely why nobody noticed.

The fix was one line:

rm wp-content/object-cache.php

The second pass

Months later I went back, looking for something else entirely, and found twenty-five rows in each of five databases left behind by ManageWP — a remote site-management service connected under the previous host. ManageWP is a legitimate product and a good one. It is not the problem here. What it left behind is.

mwp_public_keys                   present
mwp_service_key                   present
mwp_communication_keys            present
mwp_pro_connected                 'pro_sites'
mwp_new_communication_established '1'

Those are credentials and trust state for a service that can install plugins, change user accounts, and push updates. The sites were still holding the keys and still recording the relationship as active.

What it was not

This is the part most write-ups of this kind overstate, so let me be exact. The plugin that reads those keys was gone. Absent from every site. Nothing was listening, nothing was calling out, and nobody had used them. This was not a breach and it was not an open door.

It was a door frame with the lock still keyed to somebody else.

If that plugin ever came back — through a bundled theme, a host convenience tool, or somebody installing what looks like the right thing — the site would re-establish the trust relationship with whatever account still held the other half. It would not ask first. That is what the keys are for.

One hundred and eighteen rows across five sites. The sixth was clean. Same migration, same week — and nothing in the outcome revealed the difference.

The third pass

Having found one class of leftover, I went looking for the general case: settings belonging to software that is no longer installed. Not what was there, but what had been there once.

  • 91 rows from a forms plugin, on five sites that do not have it
  • 16 rows from a mail plugin, on a site that does not have it
  • 14 rows from Jetpack, on a site that does not have Jetpack
  • 8 rows from a page builder, on four sites that do not have it

Another 129 rows. None of it dangerous. All of it read on every page load that touches the options table, and none of it doing anything at all.

Deactivating a plugin does not remove its settings. Deleting a plugin usually does not either — most do not clean up after themselves, and the ones that do often ask first and default to no. This is normal and it is not anybody’s fault. It is simply what a site accumulates over years of trying things.

Why none of it announced itself

Because nothing broke. That is the whole answer.

Everything that fails visibly gets fixed, because somebody complains. A page that will not load, an email that bounces, a form that does not send — those get attention within the hour.

What survives is everything that fails quietly, or does not fail at all and simply sits there. A migration that reports success is a migration nobody looks at again. There was no symptom, on any of these sites, that would have led anyone to any of it.

How to check your own

If your site has moved hosts in the last few years, or has been looked after by somebody who no longer looks after it, this is worth twenty minutes.

Start from what is installed, not from what you find. List your active plugins, then look in the options table for names that do not match any of them. Old caching layers, old management agents, old security suites, old page builders, old form plugins. The pattern is always the same: rows named after something with nothing left on the site to read them.

Most of it will be harmless clutter. What you are actually hunting is the small subset that is not: anything resembling a key, a token, a secret, or a connection state. Those describe a relationship with something outside your site, and a relationship you did not know you had is the one worth ending.

Check the scheduled tasks as well. Cron entries pointing at code that no longer exists will sit there indefinitely, failing silently on a schedule.

Export before you delete. Everything you remove should be recoverable, because the point is to leave the site cleaner, not to discover what something did by taking it away.

The point

These are my own sites, not a client’s, and that is deliberate. I am the one who is supposed to catch this. It still took three passes, and the second and third only happened because somebody asked a question that made me look again.

That is not a confession. It is the argument. Nobody finds this sort of thing by waiting for a problem, and nobody finds all of it the first time. You find it by going and looking, on a day when nothing is wrong, at a system that is working perfectly — and then going back and looking again.

Which is most of what I do.

Leave a Reply

Your email address will not be published. Required fields are marked *