SEO & redirects
Keep your search rankings after switching — how to map WordPress URLs to Contensio routes and set up 301 redirects.
Switching platforms is the moment most sites lose search rankings. Most of that loss is avoidable with the right redirect strategy. This page covers what changes, what to do, and how to do it.
What changes between WordPress and Contensio
| URL type | WordPress default | Contensio default |
|---|---|---|
| Blog post | /year/month/day/slug/ or /?p=123 |
/slug |
| Page | /page-slug/ |
/page-slug (no trailing slash) |
| Category archive | /category/name/ |
/category/name |
| Tag archive | /tag/name/ |
/tag/name |
| Author archive | /author/username/ |
/author/{id} |
| Feed | /feed/ |
/feed |
| Attachment pages | /attachment-slug/ |
Not available |
The exact shape of your WordPress URLs depends on your permalink setting. Check Settings → Permalinks in your WordPress admin before migrating.
Option 1 — Match your WordPress URL structure in Contensio
The cleanest approach: configure Contensio routes to match your existing WordPress URL structure so no redirects are needed at all. This is only worth doing if your WordPress URL structure is clean (e.g., /%postname%/ or /%category%/%postname%/).
Contensio currently uses
/{slug}for posts and/page/{slug}for pages. Custom route prefixes are on the roadmap. If your WP structure was/%postname%/you're already aligned.
Option 2 — 301 redirects at the server level
For anything that can't be route-matched, add 301 redirects in your web server config.
Nginx
# WordPress date-based URLs → Contensio slugs
rewrite ^/(\d{4})/(\d{2})/(\d{2})/([^/]+)/?$ /$4 permanent;
# WordPress category URLs (if your Contensio taxonomy route differs)
rewrite ^/category/(.+)/?$ /category/$1 permanent;
# WordPress tag URLs
rewrite ^/tag/(.+)/?$ /tag/$1 permanent;
# WordPress author by login → author by ID (needs a lookup table)
# Better handled in application middleware — see Option 3
Apache (.htaccess)
RewriteEngine On
# Date-based post URLs
RewriteRule ^(\d{4})/(\d{2})/(\d{2})/([^/]+)/?$ /$4 [R=301,L]
# Category prefix
RewriteRule ^category/(.+)/?$ /category/$1 [R=301,L]
# Tag prefix
RewriteRule ^tag/(.+)/?$ /tag/$1 [R=301,L]
Option 3 — Redirect middleware in Contensio
For more complex redirects (e.g., author username → author ID, old numeric IDs, or hundreds of custom URLs), a Laravel middleware is the right tool.
Create a plugin with a redirect middleware:
// src/RedirectMiddleware.php
namespace MyPlugin\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RedirectMiddleware
{
// Map old WordPress URLs to new Contensio URLs
private array $map = [
'/about-us/' => '/page/about',
'/contact-page/' => '/page/contact',
'/old-post-slug/' => '/new-post-slug',
];
public function handle(Request $request, Closure $next)
{
$path = '/' . ltrim($request->path(), '/');
if (isset($this->map[$path])) {
return redirect($this->map[$path], 301);
}
// Handle WordPress date-based URLs: /2023/04/15/slug/
if (preg_match('#^/(\d{4})/\d{2}/\d{2}/([^/]+)/?$#', $path, $m)) {
return redirect('/' . $m[2], 301);
}
return $next($request);
}
}
Register it in your plugin's service provider:
public function boot(): void
{
app(\Illuminate\Routing\Router::class)
->pushMiddlewareToGroup('web', \MyPlugin\Http\Middleware\RedirectMiddleware::class);
}
Meta tags and structured data
Contensio provides SEO fields on every content item: meta title, meta description, and OG image. These map directly to WordPress's Yoast/RankMath fields.
In your theme's layout.blade.php:
<title>@yield('title', $site['name'])</title>
<meta name="description" content="@yield('meta_description', $site['tagline'] ?? '')">
<meta property="og:title" content="@yield('og_title', $site['name'])">
<meta property="og:description" content="@yield('og_description', '')">
@if($site['og_image_url'])
<meta property="og:image" content="{{ $site['og_image_url'] }}">
@endif
In each content template:
@section('title', apply_filters('contensio/frontend/page-title', $translation->meta_title ?: $translation->title . ' — ' . $site['name'], $content))
@if($translation->meta_description)
@section('meta_description', $translation->meta_description)
@endif
XML sitemap
Contensio does not generate a sitemap by default. Options:
- A plugin — build a simple sitemap route in a plugin that queries published content
- A static generator — generate a
sitemap.xmlas part of your deploy pipeline - Wait for the built-in sitemap (roadmap)
A minimal sitemap route in a plugin:
// routes/web.php in your plugin
Route::get('/sitemap.xml', function () {
$posts = \Contensio\Models\Content::published()
->with('translations')
->get();
return response()->view('myplugin::sitemap', ['posts' => $posts])
->header('Content-Type', 'application/xml');
});
RSS feed
Contensio generates an RSS feed at /feed automatically. If your WordPress feed was at /feed/, set a redirect:
rewrite ^/feed/?$ /feed permanent;
Post-migration SEO checklist
- Submit updated sitemap to Google Search Console
- Check all old post URLs redirect with 301 (not 302)
- Verify canonical tags are present on every page
- Check that meta titles and descriptions are populated for top pages
- Confirm OG image and twitter card tags are present
- Check robots.txt is correct (
/robots.txt— Contensio serves a default one) - Monitor Google Search Console for 404s over the first 4 weeks
- Test the RSS feed URL in a feed validator