Conquer Your PCOS: Rebuilding a 13-Year-Old WordPress Site Without Losing a Single URL
Conquer Your PCOS Naturally (opens in new tab) is Dr Rebecca Harwin's health publication for women with polycystic ovary syndrome. She is a chiropractor and published author, and she has been writing there since 2012, publishing more than 300 articles, many of them ranking for years.
The site had reached the point where it could not be safely updated. It ran Genesis with WPBakery on PHP 7.4, with 48 active plugins, three of which were competing caching and optimisation tools. Updating any one thing risked breaking another. The rebuild was not a redesign. It was a necessity.
The constraint that shaped everything: thirteen years of accumulated search authority sat on those URLs. A redesign that lost them would have cost more than the old site ever did.
Before and after
The old homepage, archived by the Wayback Machine in October 2025, running Genesis with WPBakery:

The same page after the rebuild, on stock WordPress with Astra and native Gutenberg blocks:

The visual change was not really the point. The old page carried a slider, a shopping cart it no longer used, and a page builder loading its full framework on every request. The new one is markup a person can edit without a builder, which is what made the speed result possible.
Context
The brief was to move from Genesis + WPBakery to stock WordPress with Astra and native Gutenberg blocks, on PHP 8.2, with no page builder of any kind. The client's priorities, in her own order: the site could not be safely updated as it stood, it was slow, and its search rankings had to survive.
Three facts made this harder than a normal migration.
The page builder's reach was unknown. WPBakery had been in use since 2018 and the client could not guarantee it was confined to pages. Shortcodes left behind by a removed builder render as visible garbage, such as [vc_row] printed in the middle of an article, and they do it silently.
The database carried personal data. The old install had accumulated a retired WooCommerce store, a form-submission archive, and a mail log.
There was no staging environment, no version control, and no record of what any of the 48 plugins were actually doing.
Permalinks as a hard constraint
The single rule that governed the build: every published post keeps its URL, byte for byte. Not "mostly", not "the important ones".
That meant permalink structure had to be set to /%postname%/ before the import, not after. WordPress assigns slugs on insert, and correcting them afterwards is a different job with different failure modes.
It also meant proving it rather than believing it. I wrote a verification script that compared every published post's URL against the pre-migration export and exited non-zero on any mismatch:
Posts: 264 total / 255 published (exact match)
Permalinks verified: PASS. All 255 published post URLs preserved
I tested the script in both directions: it passed on a clean import, and it correctly failed with a non-zero exit when I deliberately induced a slug change. A verifier that has never failed is not evidence.
That check caught three permalink defects during the build that no amount of clicking around would have surfaced.
Extracting the page builder
Twenty-two of 130 pages carried WPBakery markup. I wrote a converter that walked the raw post_content and translated the shortcodes into Gutenberg blocks: vc_column_text to paragraphs, vc_single_image resolved through the attachment ID to a real image block, vc_raw_html base64-decoded. Crucially, it flagged anything it could not convert rather than dropping it.
The interesting part was the audit that followed. My first pass sampled 28 pages, found nothing, and reported the site clean. That was wrong. A full scan, covering every published post and page queried against WordPress's registered-shortcode list and then every suspect URL HTTP-tested against rendered output, found:
- 36 items holding unregistered shortcode tokens in the database
- 26 of those already redirected away, so no visitor reached them
- 10 reachable but rendering nothing
- 2 real defects the sample had missed, including a page with 29 raw WPBakery shortcodes that the client had specifically asked to keep
Sampling tells you a site is probably fine. It is not the same as knowing.
Excluding what shouldn't move
Before the import ran, a filter stripped 777 records from the export: 48 legacy shop orders carrying customer billing details, 479 archived form submissions, and 250 mail-log entries. The script asserted that none of it survived into the new database.
A migration is a good moment to not carry personal data forward. The retired store had been closed for years; there was no reason for its customer records to exist on the new install.
What the plugin cull actually looked like
Going from 48 active plugins to 9 is easy to write and easy to get wrong. The rule I worked to: nothing gets deleted until I can show what depended on it.
That meant scanning for each plugin's shortcodes across all 255 posts and 130 pages first. Shortcodes Ultimate, for example, looked significant. Two live usages turned out to be YouTube embeds, which became native core/embed blocks. The plugin left; the videos stayed.
Three overlapping performance plugins were a large part of why the site was slow. They were doing the same work three times and occasionally undoing each other.
Performance
Measured with Google's own Lighthouse, before and after, on the live site:
| Page | Mobile before | Mobile after | Desktop before | Desktop after |
|---|---|---|---|---|
| Homepage | 38 | 100 | 68 | 100 |
| About | 50 | 97 | 81 | 99 |
| What Is PCOS | 48 | 98 | 83 | 99 |
Core Web Vitals mattered more than the score. Every page had been failing both LCP and CLS. Afterwards every page passed both, with layout shift at zero.
The homepage went from taking 9.8 seconds to render its main content on a phone to 1.4 seconds.
One desktop run returned 78 where three consecutive re-runs returned 100. I noted the outlier in the client's report rather than quietly dropping it.
Go-live
The switch was designed so that rollback was a directory rename. The new site kept its own database, which meant the old database was never modified and remained a complete, untouched fallback. Downtime was under two minutes, at 3am in the client's timezone.
Afterwards, the full 1,370-URL verification ran against the live domain:
200 OK : 1270
redirected: 100
404 : 0
PASS: every pre-migration URL still resolves.
A fix worth more than the rebuild
Once the site was stable, the client asked whether anything obvious could be improved for search. Rather than answer from general principles, I pulled her Search Console export.
The site was earning 124,080 impressions and 359 clicks, a 0.29% click-through rate where 2% to 5% is normal. She was being shown constantly and almost never clicked.
Her single most-seen page, on cherry angiomas and oestrogen dominance, had 21,780 impressions and 46 clicks. The cause turned out to be one word:
| Spelling | Distinct queries | Impressions |
|---|---|---|
estrogen (American) | 51 | 9,922 |
oestrogen (British) | 1 | 690 |
A 14:1 ratio. Her title used the British spelling, so for the searches that actually had volume, the word the reader typed never appeared in her result. There was nothing to bold, nothing to signal relevance. She was ranking fourth for some of these and still getting 0.3%.
The fix was to carry both spellings in the title. It affected five articles, not one. No URL changed. Those pages hold thirteen years of authority, and the title is not the address.
The same audit found 943 tag archives submitted to Google for 255 articles, 740 of them wrapping a single post, with one actively competing against the article it duplicated. Those were noindexed, the 391 empty ones deleted, and the nine that held exactly one post and had real traffic were redirected into that post so the accumulated signal consolidated rather than evaporating.
What the client said
"Thanks for going above and beyond and for making this project easier than I worried it might be."
Dr Rebecca Harwin, Conquer Your PCOS Naturally
That sentence is the one I care about most. A rebuild of a site someone has spent thirteen years on is frightening, and the anxiety is rational, because plenty of them go badly.
Outcome
- 300+ articles migrated with every URL preserved, verified rather than sampled
- 48 active plugins reduced to 9, each removal justified against what depended on it
- PHP 7.4 → 8.2.33, the upgrade the old stack could not take
- Homepage 38 → 100 on mobile; every page passing Core Web Vitals
- 777 personal-data records excluded from the migration
- Zero broken URLs, confirmed by a full re-crawl after every subsequent change
The site is now a stock WordPress install that its owner can update herself without fear, which was the actual point.