Reviving a 12-Year-Old WordPress Blog: A Migration Audit

Written by

in

,

Why the blog went silent

This blog has been quiet since 2014. Not because I stopped tinkering — the homelab kept growing (TrueNAS, UniFi, Proxmox/KVM, solar automation) — but because life happened and the site sat through a WordPress migration that never quite finished. This month I finally brought it back, and the process of auditing a 12-year-old migrated blog turned up enough lessons to be worth writing down.

If you’ve ever moved a WordPress site between hosts, domains, or servers, most of this will look familiar.

The audit: what I found

I connected an AI agent to the site through WordPress’s REST API and did a full inventory: 45 published posts, 4 pages, 52 tags, 16 categories. That sounds healthy. It wasn’t. Here’s what a migration leaves behind:

1. Duplicate posts

The migration had re-imported content without deduplicating, so five posts existed twice with identical titles — Google Chromecast, iPad 2 review, clearing Linux cache, Linux disk quotas, and SSH brute-force protection with iptables. Both copies were live, both were indexed. The fix: pick the newer revision of each pair and trash the other. Trash, not delete — WordPress keeps it recoverable.

2. A media library that lost its media

Every content image pointed at the old domain, wongtek.com, which no longer resolves. The media library itself was empty — zero items. The posts carried dead <img> tags from 2012. Lesson: check your media library after any migration, not just your posts.

3. Internal IPs published to the internet

This one mattered. Two pages still linked to http://192.168.10.5:30040/... — my LAN IP and a port, published to the world over plaintext HTTP. These were broken links for visitors and free reconnaissance for anyone curious. Migration tools love to bake absolute internal URLs into content. Grep your database for RFC1918 addresses:

grep -rn "192.168.\|10\.0\.\|172.1[6-9]\.\|172.2[0-9]\.\|172.3[01]\." wp-content/

4. Taxonomy sprawl

52 tags for 45 posts, including gems like USB3, USB 3.0, and UCSD 3.0 — the last one being a typo of the second. Also VirtualBox/Virtual Machine/vm, and cars/Civic/Honda Civic. Twelve tags had zero posts. Merging tags on old content is tedious; deleting unused ones takes seconds.

5. Placeholder junk pretending to be content

“Hello world!” and the default Sample Page were still live, twelve years in. Fresh installs always ship these; migrations always keep them.

How I checked every link

I wrote a small auditor that pulls every post and page through the REST API, extracts every href and img src, and probes each target:

curl -s "https://example.com/wp-json/wp/v2/posts?per_page=100&_fields=id,link,content"

After the cleanup the site went from 45 posts to 39, with zero 404s in the content. The audit script re-runs any time I change anything.

REST API quirks worth knowing

Two things cost me time:

  • Trashing via REST: POST /wp-json/wp/v2/posts/123 {"status":"trash"} fails with rest_invalid_paramtrash isn’t in the status enum. Use DELETE /wp-json/wp/v2/posts/123 without force=true instead; that moves it to trash.
  • Pages are not posts: the same ID can exist in both namespaces. Trashing a page through the posts endpoint silently does nothing.

Application passwords, not admin passwords

For API access I created a WordPress application password (Users → Profile → Application Passwords) instead of using the admin password. It’s revocable in one click, works over basic auth, and is scoped to one user. If you’re wiring scripts or agents to WordPress, this is the way:

curl -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
  https://example.com/wp-json/wp/v2/users/me?context=edit

What’s next

The site is clean now: 39 posts, no duplicates, no dead images, no internal IPs leaking, taxonomy pruned. The bigger job is the 12-year gap itself — so I’m aiming to publish something every other week from here on. The homelab has been busy: a self-hosted AI agent on KVM, MCP integrations into TrueNAS and UniFi, and home battery automation with Home Assistant. Those stories are coming.

Same practical tone as the old posts. Same promise: only commands I’ve actually run.

Comments

Leave a Reply