Your first Hugo build succeeds, but an old bookmark returns 404 and half the images are missing. The build checked whether it could render the new input. It didn’t know what the old site promised its readers.
We’ll migrate one representative post and turn those promises into checks before moving the whole collection.
Make an inventory before conversion
A content export is not a complete site backup. Keep the database and relevant files separately and verify your recovery method. The WordPress backup guide explains those components.
Choose a post containing a heading, image, code example, and internal link. Record its current public URL, referenced media URLs, title, publication state, and any redirects. This small specimen exposes more conversion problems than a paragraph of plain text.
The diagram treats normalization and verification as explicit stages between export and cutover.
Work through one URL contract
Suppose the old post lives at:
/2020/06/03/example-post/
In your working Hugo site, create content/posts/example-post.md:
---
title: "A migration test"
date: 2020-06-03T09:00:00Z
draft: true
url: /2020/06/03/example-post/
---
This paragraph is a migration marker: amber-post-17.

Put an actual test image at static/images/migration-demo/chart.svg. The URL is explicit, so its behavior doesn’t depend on the file’s directory matching your old permalink structure. Hugo’s URL management reference describes url and aliases.
Start a local draft preview from the site root:
hugo server --buildDrafts --bind 127.0.0.1 --port 1313
In another terminal:
curl --fail http://127.0.0.1:1313/2020/06/03/example-post/
curl --fail http://127.0.0.1:1313/images/migration-demo/chart.svg
The first response should contain amber-post-17. The second should return the image. Then inspect the page in a browser: successful HTTP responses alone won’t catch a broken layout or unreadable code sample.
Pause and predict
The new /posts/example-post/ page works, but the old dated URL fails. Is the migration complete?
Need a hint?
Readers and search engines may still use the original address.
Show the reasoning
Convert content with a known input and output
Choose an exporter that matches the input you have. A WordPress plugin exporter and an XML-to-Markdown converter are different workflows. Inspect their documented invocation and output before applying one across the whole site.
For the specimen, compare heading structure, code fences, image paths, captions, and internal links with the original. Strip obsolete editor markup only after confirming it doesn’t contain meaningful content.
Make a simple migration ledger:
| Old item | New location | Verification |
|---|---|---|
| Post URL | Explicit front matter URL | Expected text at original path |
| Embedded image | Static asset path | Image loads and has useful alt text |
| Internal link | Retained URL or redirect | Destination exists |
| Unpublished post | Draft content | Absent from ordinary build |
Verify two builds
A draft preview helps review unfinished content. An ordinary build answers a different question: what would be published?
hugo --destination /tmp/hugo-migration-published
Use a dedicated empty destination for this exercise. Confirm that the test draft is absent from that output. When you deliberately publish the specimen, change its draft flag and rebuild, then verify the intended route.
The Hugo server reference documents preview behavior. If you still need a working starter site, follow Hugo’s quick start before migrating content; a new directory without templates isn’t a complete rendering setup.
Try an edge case
Apply the idea
An old image URL is referenced by other websites. Is copying the image under a new filename enough?
Need a hint?
Those callers don't know your new path.
Show the reasoning
Only expand the conversion after the specimen passes. At cutover, account for edits made since the export and repeat the checks against the actual hosting path. Keep a recovery plan until the migrated content and URLs have been verified.