Even if you are not using the NextJS framework, Vercel is a great option for running your static or nodejs applications. It is super easy to get started, and its fast and free. This guide is intended to show you some common configuration settings for your production application.

Rewrites for Single Page Application

When I first deployed my Simple Page Application (SPA) to Vercel, it looked great. But when I refreshed the page on any page but the homepage, I received an error page - 404 - This page could not be found. The url was randombits.dev/articles, so the server was looking for a file called /articles/index.html. But I have only 1 html file at the root location, so it gave the 404 error.

The solution is easy. You need to tell Vercel to rewrite the request to the root index.html file no matter what path was asked for:

{
  "rewrites": [
    { "source": "/(.*)", "destination": "/" }
  ]
}

Vercel will prioritize the file system first: if the path exists, it will return the file, Otherwise, it will rewrite the url. That way you can keep the rule simple and still fetch static assets like JS and image files.

If you have apis deployed on the same host, you can change the rewrite to the following:

{
  "rewrites": [
    { "source": "/(?!api\/.*).*/", "destination": "/" }
  ]
}

This will match everything except the /api path.

Deploying Serverless Functions

If you have an api directory in your project, Vercel will automatically deploy the contents as serverless functions. The path of the API endpoint will be the same as the path of the file system. For example:

  • /api/index.js => /api
  • /api/login.js => /api/login
  • /api/user/index.js => /api/user
  • /api/user/[id].js => /api/user/45

Serverless Function example:

import type {VercelRequest, VercelResponse} from '@vercel/node';

export default function (request: VercelRequest, response: VercelResponse) {
  response.status(200).json({
    text: `Hello from ${request.url}`
  });
}

Deploying Edge Functions

Edge functions are deployed to all regions and are faster than serverless functions, but have a limited API, and must have a small build. They are defined using the same file path scheme as serverless functions, but the function is very different:

export const config = {
  runtime: 'edge',
};

export default function MyEdgeFunction(
  request: Request
) {
  return new Response(`Hello, from ${request.url} I'm an Edge Function!`, {
    headers: {
      'my-header': '123'
    }
  });
}

Change the base /api path

Unfortunately, you cannot change the base /api path. However, you can rewrite the url in your vercel.json settings file:

{
  "rewrites": [
    { "source": "/rest", "destination": "/api" }
  ]
}

Changing Caching Rules

By default, Vercel puts a max-age of 0 on all assets, which means the browser won’t cache them. The reason for this is so that when a new deployment is made, users get the new version right away.

But if your like me, and you use proper cache busting techniques, you will want to change these settings. I have webpack generating the content hash for the file name of all my JS/CSS files:

output: {
  filename: "[contenthash].js" // d0fb3aa6b14ecb0578b5.js
}

The filename is generated based on the content of the file, so if the content changes, the name will change. When I update code and redeploy, the file name is different, so the cached content cannot be returned to the user. Therefore, you can change the Cache-Control settings to use a very long max-age value, and the immutable keyword, effectively telling the browser the file will never change.

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

The reason you need both max-age and immutable is that the immutable keyword is not well-supported in browsers yet.

The current example is modifying the cache settings for all served files. But by changing the source pattern, you can adjust which files you want to target.

Redirects when you move a page

If you move a page to a different path, and there are already external links to your page, you will want to add a redirect:

{
 "redirects": [
    {
      "source": "/my-old-path",
      "destination": "/my-new-path",
      "permanent": true
    }
  ]
}

The permanent flag tells Vercel which HTTP status code should be returned:

permanent: false = 307 temporary redirect

permanent: true = 308 - permanent redirect

Closing Thoughts

While Vercel is a great experience for simple applications that fit the typical nodejs structure, its lack of customizability for building/deploying can be frustrating for more complex applications. If you want to have full control over your rewrites, api paths, headers, etc, I would suggest a more full-featured platform like Google Cloud Run.