How toAdd dynamic canonical URL to Remix routes
Canonical URLs is the original and official URL of a document, used to tell search engines which one is the real URL.
As an example if you have the URL /list?sort=asc
you can set the canonical URL to be /list
so Google only index the /list
version, since the content of both is most likely the same we want Google to only index one.
This can also be used to ensure that if Google finds URLs like /article?utm_source=newsletter
it will not consider it a duplicate of /article
.
To set a canonical URL we need to the the <link rel="canonical" href="url" />
tag, but because the href
needs to be the full URL and not just the path we need to know the host of the request.
So we will need to use MetaFunction
instead of LinksFunction
in our Remix's route.
app/routes/article.tsx import type { LoaderFunctionArgs, MetaDescriptor, MetaFunction } from "@remix-run/cloudflare"; import { json } from "@remix-run/cloudflare"; export const meta: MetaFunction<typeof loader> = ({ data }) => data?.meta ?? []; export async function loader({ request }: LoaderFunctionArgs) { return json({ meta: [ { tagName: "link", rel: "canonical", href: request.url } ] satisfies MetaDescriptor[]; }); }
By doing this we can use the request.url
as the canonical URL, but this will use the whole URL, including search params. We can remove them instead.
- { tagName: "link", rel: "canonical", href: request.url }
+ { tagName: "link", rel: "canonical", href: new URL("/article", request.url).toString() }
With this change, we can use the request.url
as base for the protocol and host, but replace the rest with /article
.
We can also instance URL and then remove the search params which can be useful to keep route params.
export async function loader({ request }: LoaderFunctionArgs) {
+ let url = new URL(request.url);
+ url.searchParams.forEach((_, key) => url.searchParams.delete(key));
return json({
meta: [
+ { tagName: "link", rel: "canonical", href: url.toString() }
] satisfies MetaDescriptor[];
});
}