# Add dynamic canonical URL to Remix routes

Used: @remix-run/node@2.0.0, @remix-run/cloudflare@2.0.0, and @remix-run/deno@2.0.0

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](https://remix.run)'s route.

```ts {% path="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.

```diff
-      { 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.

```diff
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[];
  });
}
```