Migrating from WordPress to Hugo: A Practical Walkthrough
Migrating a WordPress site to Hugo is one of the most common transitions in publishing infrastructure. The reasons vary — performance, hosting cost, maintenance overhead, security exposure — but the path through the migration is broadly the same regardless of why you are making the move. This is a practical walkthrough of what the process actually involves.
What You Are Gaining and What You Are Giving Up
Before starting, be clear about the tradeoffs. You gain speed, security, and dramatically simplified hosting. You give up the WordPress admin UI, browser-based editing, dynamic features (comments, contact forms, membership), and the plugin ecosystem. If any of those WordPress features are essential to your editorial workflow, plan how to replace them before migrating.
Comments can be replaced with Disqus, giscus, or a self-hosted Commento instance. Contact forms work through Netlify Forms, Formspree, or similar services. Membership and paywalled content require a third-party service like Memberstack or a separate application layer.
Step 1: Export WordPress Content
WordPress provides an export tool under Tools → Export that produces a WXR file (WordPress eXtended RSS) — an XML document containing all your posts, pages, categories, tags, and metadata.
Export the full site. If your site has media attachments, note that the WXR file contains links to your media, not the files themselves. You will handle media separately.
Step 2: Convert WXR to Markdown
Hugo content is Markdown with YAML or TOML frontmatter. You need to convert your WordPress XML export into that format.
The most reliable tool for this is wordpress-to-hugo-exporter, a WordPress plugin you run before deactivating WordPress. Install it, navigate to Tools → Export to Hugo, and it produces a ZIP file of Markdown files with frontmatter already populated from your post metadata.
Alternatively, if you prefer to work from the WXR file directly:
npm install -g wp2hugo
wp2hugo --input export.xml --output content/
Either approach produces a content/posts/ directory populated with .md files. Review a handful of them to confirm the conversion quality before proceeding.
Step 3: Handle Media
Your WordPress media library lives at wp-content/uploads/. You need this directory.
If you have access to the WordPress server, copy the uploads/ folder to your Hugo project’s static/ directory. Hugo copies everything in static/ to the root of the built site, so your existing image paths (typically /wp-content/uploads/YYYY/MM/filename.jpg) will resolve correctly without URL rewrites.
If you do not have server access, use wget to mirror the uploads directory:
wget -r -no-parent -nd -A jpg,jpeg,png,gif,webp,pdf \
https://yoursite.com/wp-content/uploads/ \
-P static/wp-content/uploads/
Step 4: Set Up Hugo
If you have not already installed Hugo:
# macOS
brew install hugo
# Linux
sudo apt install hugo
Initialize a new Hugo site:
hugo new site my-publication
cd my-publication
Copy your converted Markdown files into content/posts/. Choose and install a theme, or begin building a custom one in layouts/.
git init
git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
Set the theme in hugo.toml:
baseURL = "https://yoursite.com"
languageCode = "en-us"
title = "Your Publication"
theme = "PaperMod"
Step 5: Handle Redirects
Any URLs that change during the migration — and some will — need redirects to preserve SEO and existing inbound links.
WordPress typically uses URLs like /year/month/slug/. Hugo uses whatever URL structure you configure. If you want to preserve the WordPress URL structure exactly, set your Hugo permalink format accordingly:
[permalinks]
posts = "/:year/:month/:slug/"
For URLs that cannot be preserved, create a static/_redirects file (for Netlify) or configure redirects in your hosting platform. Map each old URL to its new equivalent.
Step 6: Audit and Test
Run hugo server and work through the site systematically:
- Spot-check converted posts for formatting issues (HTML that did not convert cleanly to Markdown, broken shortcodes, malformed frontmatter)
- Verify image paths resolve
- Check that taxonomy pages (category and tag archives) generate correctly
- Confirm your RSS feed generates at
/index.xml - Run the site through a link checker to surface 404s before launch
hugo server -D
# Then in another terminal:
npx broken-link-checker http://localhost:1313 --recursive
Step 7: Deploy
Hugo’s build output is the public/ directory — a complete static site ready for deployment. Push it to Netlify, Cloudflare Pages, GitHub Pages, or any static hosting service.
For Netlify, the build configuration is minimal:
# netlify.toml
[build]
command = "hugo"
publish = "public"
[build.environment]
HUGO_VERSION = "0.145.0"
Connect your repository, push, and Netlify handles the rest.
After Migration
Once the new site is live, keep the old WordPress installation running (but not publicly accessible) for a few weeks while you verify everything transferred correctly. Monitor your server logs and Search Console for 404 patterns. Clean up any stragglers with additional redirects before decommissioning WordPress entirely.
The migration is not trivial, but it is well-trodden ground. Most content-only WordPress sites migrate cleanly in a focused day of work. Sites with complex custom post types or heavy shortcode usage take longer.