5 min read

Add Response Headers to Redirects in Vercel

by Daniel Nagy @danimal_channel
Young tiger at the beach working on a computer, digital art

Today I will share with you my use case for adding response headers to redirects and how I use Vercel's edge middleware to add response headers to redirects.

My Use Case#

My blog doesn't have a home page yet. Rather than display an empty page, I redirect the user to my about page. In my vercel.json file, that looks like this:

{
  "redirects": [
    { "source": "/", "destination": "/about", "permanent": false }
  ]
}

This turns out to be problematic for search engines, though. Looking at Google Search Console, I can see that Google refused to index my home page because it redirected to my about page. In addition, Google refused to index my about page because it decided the URL for my home page was the canonical URL for my about page.

To fix this, I need to tell Google that https://danielnagy.me/about is the canonical URL for my about page. One way I can do that is by adding a Link HTTP header to the response. However, it turns out this is not possible using Vercel's configuration. This feature has been requested in this Next.js discussion. I also tried using the headers configuration, matching the redirected routes, but that did not work either.

My Solution#

It is not well documented, but you can create an edge middleware function by placing a middleware.ts file in the same directory as your vercel.json file. This file can export a default function that takes a Request object as input and returns a Response object as output. These are web-standard Request and Response objects. It's not documented, but you can tell Vercel to continue to the next function in the middleware stack by setting the x-middleware-next HTTP header in the response, as noted in this comment and as can be seen by looking at the source code for Next.js.

Using this information, I can implement the redirect logic using a middleware function, allowing me to add headers to the response.

const next = (): Response => {
  return new Response(null, {
    headers: { "x-middleware-next": "1" }
  });
};

const redirect = ({
  headers,
  permanent = true,
  url
}: {
  headers?: HeadersInit;
  permanent?: boolean;
  url: string | URL;
}): Response => {
  return new Response(null, {
    headers: {
      Location: String(url),
      ...headers
    },
    status: permanent ? 308 : 307
  });
};

export default function middleware(req: Request) {
  const url = new URL(req.url);

  switch (url.pathname) {
    case "/": {
      const redirectUrl = new URL("/about", url.origin);

      return redirect({
        headers: {
          Link: `<${redirectUrl}>; rel="canonical"`
        },
        permanent: false,
        url: redirectUrl
      });
    }
    default:
      return next();
  }
}

As far as I can tell, it is not possible to have more than one middleware function. Also, middleware functions can only proxy incoming requests; they cannot proxy outgoing responses 😞. Furthermore, edge functions can run on a variety of hardware devices and therefore only support a subset of Node.js APIs.

I'm not an SEO expert, and I don't know if this has fixed my issue with Google yet, but your use case for adding response headers to redirects may be different from mine, and I hope this information helps!

Written by Daniel Nagy

Daniel is a software engineer and full-stack web developer. He studied computer science at Ohio University and has been doing web development and hybrid mobile app development for over 8 years.

Comments#

INFO
You will not receive notifications for new comments. If you are waiting for a reply, please check back periodically.
Markdown enabled