On WordPress, redirects are a plugin or a few lines of .htaccess. On a static site there’s no PHP and no .htaccess — so how do you 301 your old URLs to the new ones? This is the single most important step for keeping your rankings whenever a URL changes during a migration. Here’s how to do it properly on a static host.
First rule: preserve URLs so you barely need redirects
The best redirect is the one you never have to write. If your static site is rebuilt on the same permalinks WordPress used, the large majority of your URLs don’t change at all — and Google never sees a 404. Redirects are only for the URLs that genuinely must change. (This is the foundation of not losing rankings when you migrate.)
When you actually need a 301
The usual WordPress-to-static cases where a URL changes:
- Permalink style change — e.g. moving from “plain”
/?p=123links or date-based/2019/04/my-post/to a clean/my-post/. - Trailing-slash normalization — deciding between
/my-postand/my-post/. - WordPress-only URLs that won’t exist on static —
/feed/,/comments/feed/, attachment pages,/author/...,?replytocom=. - Archives you’re consolidating — tag or category pages you’re not keeping.
How redirects work without a server
There’s no .htaccess on a static host. On Cloudflare Pages you ship a plain-text _redirects file at the root of your site. One rule per line — from to status:
/old-post/ /new-post/ 301
/2019/04/hello/ /hello/ 301
/feed/ / 301
/blog/* /articles/:splat 301
A few things that matter:
- Always 301 for migrations — it’s permanent and passes link equity. Avoid 302 (temporary), which doesn’t.
*is a splat, referenced in the destination as:splat;:namedplaceholders also work.- Order matters — the first matching rule wins, so put specific rules above wildcards.
- Static files take precedence over redirects, and query-string-only URLs like
/?p=123can’t be matched by_redirectsalone — preserve those mappings at export time (or handle them with a Worker).
Jekyll gotcha:
_redirectsstarts with an underscore, so Jekyll ignores it unless you add it to theinclude:list in_config.yml. Forget that and the file never reaches your build — and none of your redirects work.
Step by step
- Build your URL map. List your old URLs (from your WordPress sitemap, the Search Console Pages report, or the XML export) and the new path each should land on.
- Write
_redirects(or have it generated for you). - Make it ship — add
_redirectstoinclude:in_config.yml. - Deploy and test a handful by hand (
curl -Ior your browser) — confirm a301and the correct destination. - Submit your new sitemap in Search Console, watch the Coverage report, and add rules for any 404s that surface. (See deploying to Cloudflare Pages for the deploy basics.)
Don’t break what already works
- Update internal links — navigation, footer, and in-content links should point to the final URL, not lean on a redirect.
- Keep redirects forever. Backlinks and old social shares keep their value only as long as the redirect resolves.
- Changing or removing a published URL later? On a CDN the edge can keep serving the old copy for a while — a fresh
_redirects301 for that exact path is the reliable way to override it.
ZeroPress sets these up for you
You don’t have to hand-map anything. ZeroPress preserves your permalinks (so most URLs never change) and configures the 301s for the ones that do, as part of the migration. (Here’s how the import works.) Combined with the SEO-preservation basics, your rankings move across cleanly — for a one-time migration with no monthly bill. (See pricing.)
Check your site first
Want to know how many of your URLs would actually change — and whether your site can go static at all? The free migration checker gives you a read in seconds, no login required.