Think You Very Much An Invitation To Think

Rescuing a Malware-Infected WordPress Blog I Abandoned in 2011

I moved my email list off Namecheap to DigitalOcean because I’m done with that hosting account. But there was one more thing on it I didn’t want to lose: RollerCoasterTour.com.

If you weren’t around for it: in 2011 I set out to ride every roller coaster in the US. One man, three months, 13,000 miles. I blogged the whole thing: 28 posts, with titles like “How I Ripped My New Shorts At Michigan’s Adventure”, “Why A Hearse?” and “In Which I Black Out at Six Flags Over Texas”. About 390 people left comments. The tour ended early after some health trouble in Texas, and I wrote “The Economics (and Official End) of Roller Coaster Tour” to call it.

Fifteen years later the site was in rough shape. It was on Namecheap shared hosting with known WordPress malware on the account. Because of that malware, I’d taken the site down, and it hadn’t been public in years. It was running WordPress 4.7 from 2017 with some risky plugins, including exec-php, which runs PHP code straight out of post content. On top of that, the domain’s DNS was dangling, and another hosting account had claimed it.

I didn’t want to fix any of that. This was pure nostalgia. I wanted the site back, at the same address, looking the way it did in 2011, and read-only, with as little on it as possible for anyone to break into.

So I had Claude do it, with the same rules I gave it for the Sendy move: ask me before any deploy, DNS change, install, or anything destructive, never print secrets, and explain things simply. The database and files were already backed up to my laptop from that job.

The big decision: don’t run the old WordPress

The normal way to turn an old WordPress site into static files is to get WordPress running again (on your own computer, say) and then download every page with a tool like wget.

Claude suggested something else: skip WordPress entirely. Read the posts and comments straight out of the database backup and write the HTML with a Python script. That way the old, possibly infected PHP code never runs at all, not even on my laptop, and there’s nothing to install. I picked that.

Claude: Wrote a small loader that turned the MySQL backup into a SQLite file. It skipped the users table, the comment-subscriber lists, and a pile of backup-plugin junk on purpose. There’s no reason to carry old password hashes and email addresses around.

Then it wrote the build script. To make the pages look like 2011, it recreated how WordPress 3.1 formatted post text (paragraphs, curly quotes and dashes, photo captions) and the exact HTML of the Thesis 1.8 theme: the menu, the sidebar, the homepage layout, threaded comments.

It got the theme details by reading the theme’s PHP files as plain text, without ever running them. That’s how it copied little things like the homepage showing one featured post and then teasers in pairs, excerpts cut at 55 words ending in ”[…]”, and a quirk in how Thesis sized photo captions.

The Wayback Machine as the answer key

The Wayback Machine had about 40 captures of the site from 2011, nearly every page. Those were the template for the HTML and also the test.

Claude: Wrote a script that compared every rebuilt post against its 2011 capture. The text matched 100% on almost every post. The leftover differences were Google Analytics tracking code (removed on purpose) and edits I’d made after a page was captured.

Fun detail: the theme’s CSS file in my backup was byte-for-byte identical to what the site served in June 2011.

Photos and videos

Most of the photos were on Flickr. After 15 years, 107 of 110 images still loaded from Flickr and Amazon S3. The other three (the route map and the About page photos) were in an S3 bucket that’s gone, and Claude recovered them from the Wayback Machine. Then it copied every image into the site itself, so the archive doesn’t depend on Flickr or S3 sticking around.

All 7 YouTube videos still play.

What’s gone, and why

The rebuilt site is plain HTML and CSS. No PHP, no login page, no scripts. Here’s what was removed or changed:

  • All the forms and widgets: comment forms, the AWeber email signup, the contact form, Google Analytics, and the Tweetmeme and Facebook Like buttons.
  • Comments stay, read-only. All the old ones are still there, you just can’t add new ones.
  • Commenters’ Gravatar pictures became plain default avatars. A Gravatar image URL is built from a hash of the commenter’s email address, and that can sometimes be reversed. My own replies keep my avatar.
  • Commenters’ emails and IPs were never even read by the build, and automated checks confirmed none show up anywhere on the site.
  • My old email address is off the Contact page.
  • Two Flash slideshows (remember Flash?) became links to the photo sets on Flickr.
  • A thin “Archived from 2011 • Comments are closed” banner sits at the top of every page.

Every URL is the same as before (/michigans-adventure/, /about/, /blog/page/2/ and so on), so old links to the site work again.

Hosting on Cloudflare Pages

This one didn’t need a server like Sendy did. It’s just files, so it went on Cloudflare Pages, which costs nothing on the free plan.

Me: Added the domain to Cloudflare and switched the nameservers at Namecheap. I also made a Cloudflare API token that could only touch this one domain, and saved it to a file on my laptop straight from the clipboard, so it never showed up on screen.

Claude: Deployed the site, connected rollercoastertour.com and www, set www to redirect to the plain domain, and turned on HTTPS everywhere with the strict SSL setting.

We also added three more DNS records. The domain doesn’t send or receive email, which makes it easy for spammers to fake email “from” it. These records tell mail servers that any email claiming to be from rollercoastertour.com is fake:

  • A “null MX” record: this domain accepts no email
  • An SPF record: no server is allowed to send for it
  • A DMARC record: reject anything that fails

When everything worked, the token was revoked.

Where it went wrong

Same as with Sendy, it wasn’t perfect:

  1. The Worker detour. I wanted a plain Cloudflare Pages site. The current version of Cloudflare’s command line tool quietly turns “create a Pages project” into a Cloudflare Worker instead, and prints a note written for AI agents, encouraging them to tell the user this is an improvement. I saw it and said “wait, I don’t want a Worker, I want this on Pages.” Claude recreated it as a classic Pages project (that takes a hidden --force flag) and deleted the Worker.
  2. Deleting the Worker also deleted the DNS records it had made, so the site briefly broke. Cloudflare’s login for the command line tool can deploy Pages but can’t create DNS records, which is why I made the scoped token.
  3. www still failed on my Mac after it was fixed everywhere else. My Mac had cached the brief “no such domain” answer. Claude’s command to clear the cache needs my password, which it can’t type in for me, so I just waited, and it cleared on its own soon after.
  4. The first version of the database loader produced nothing. It expected a space after a keyword in the backup file, and the file had a line break there instead. The empty result made it obvious, and it was fixed right away.

Making it fast

The first Google PageSpeed test on desktop gave it 83 for Performance and 77 for Best Practices. Two things were dragging it down: the YouTube player at the top of the homepage (about 1 MB of YouTube’s code plus cookies) and a 383 KB logo image.

Claude:

  • Replaced the videos with click-to-play thumbnails. They’re plain HTML, no JavaScript. The YouTube player (the privacy-friendly youtube-nocookie version) only loads when you click.
  • Shrank the logo to 67 KB with no visible difference.
  • Made images load only as you scroll to them, let browsers cache files for 30 days, and set the fonts so text shows up right away.

Now it scores 94 on mobile and 100 on desktop, and the videos still play.

The scorecard

What I did: Said yes to Claude’s ideas (rebuild from the database instead of running WordPress, add the banner, add the anti-spoofing records). Added the domain to Cloudflare, changed the nameservers at Namecheap, made and revoked the token, and caught the Worker thing.

What Claude did: Everything else. The loader, the build script, the Thesis recreation, the Wayback comparisons, rescuing the images, the privacy checks, deploying, DNS, and the speed fixes.

From handing it the backup to the site being live took about an hour or two.

Result

RollerCoasterTour.com is back, looking like 2011, fast, with no server code on it, for free. The old hosting account can be cancelled and nobody has to log into it ever again.

If you’ve got an old WordPress blog rotting on cheap hosting: keep the database, don’t trust the files, and turn it into plain HTML.