Skip to content

Custom domain & canonical

When your blog renders on your domain, you have to decide one thing deliberately: what goes in <link rel="canonical">. Get it wrong and search engines either deduplicate against the wrong URL or split ranking signal. This guide is about owning that decision.

Each article carries canonicalUrl — an absolute URL, or null if the site has no URL set. It is the canonical Ghost Writr believes in, and it may point somewhere other than where you’re rendering:

  • It may point to your blog (the common case — you set your site URL in Ghost Writr).
  • It may point elsewhere — for syndicated or adopted content, the canonical can intentionally credit another origin.

So canonicalUrl is a value to respect, not blindly stamp with your own page URL. The rule:

  • You own the canonical home → emit https://your-domain.com/blog/{slug} (the page’s own URL).
  • The article points elsewhere on purpose → keep article.canonicalUrl so you don’t claim content that canonically belongs to another origin.

Set alternates.canonical in generateMetadata. Build the URL from your own domain and the slug — that’s the “I own this” choice:

app/blog/[slug]/page.tsx
import type { Metadata } from "next";
import { gw } from "@/lib/ghostwritr";
const SITE = "https://your-domain.com";
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const article = await gw.getArticle(slug);
if (!article) return {};
// Own the canonical: your domain + slug. Swap to article.canonicalUrl
// only if you want to honor a deliberate cross-origin canonical.
const canonical = `${SITE}/blog/${article.slug}`;
return {
title: article.seoTitle,
description: article.seoDescription ?? undefined,
alternates: { canonical },
};
}