ZEIT Now uses routes to define the behavior of how a request is handled on the routing side. For example, you might use a route to proxy a URL to another, redirect a client, or apply a header with the response to a request.
By default, routing is defined by the filesystem of your deployment. For example, if a user makes a request to /123.png
, and your now.json
file does not contain any routes with a valid src
matching that path, it will fallback to the filesystem and serve /123.png
if it exists.
A route can be defined within a project's now.json
configuration file as an object within an array assigned to the routes
property, like the following which creates a simple proxy from one path to another:
{ "routes": [{ "src": "/about", "dest": "/about.html" }] }
An example now.json
file with a routes
property that proxies one path to another upon request.
Routes Properties
ZEIT Now Routes have multiple properties for each route object that help define the behavior of a response to each request to a particular path.
src
Type: String supporting PCRE Regex and Route Parameters like /product/(?<id>[^/]+)
.
For each route, src
is required to set the path which the behavior will affect.
The following example shows a now.json
configuration that takes a src
path and proxies it to a destination dest
path.
{ "routes": [{ "src": "/about", "dest": "/about.html" }] }
An example now.json
file with a routes
property that proxies one path to another upon request.
dest
Type: String
dest
is used to proxy the src
path to another path, such as another URL or Now hosted Serverless Function.
The example for the src
property shows how both methods work together to create a proxy.
{ "routes": [ { "src": "/about", "dest": "https://about.me" }, { "src": "/action", "dest": "my-serverless-function-action/index" } ] }
An example now.json
file with routes
properties that proxy paths to another upon request.
dest
path to any URL, Now hosted Serverless Function, or even non-Now hosted URLs as shown in the code above. If you don't perform any proxying, you can safely remove dest
.{ "routes": [{ "src": "/about" }] }
This will route to /about
without proxying, but routes like this are usually redundant with handle filesystem.
headers
Type: Object
The headers
property is an object supporting HTTP headers as the keys, with the intended value as the key's value.
An example of using the headers
property to add shared caching headers to all files in an images
directory:
{ "routes": [ { "src": "/images/(.*)", "headers": { "cache-control": "s-maxage=604800" }, "dest": "/images/$1" } ] }
Setting cache-control
headers for all paths under an images
directory with routes.
routes
, these are defined in the same way.continue
Type: Boolean
The continue
property allows routing to continue even though the src
was matched.
For example, you can use this property in combination with the headers
property to append headers to a broader group of routes instead of applying it to every route.
{ "routes": [ { "src": "/blog.*", "headers": { "Cache-Control": "max-age=3600" }, "continue": true }, { "src": "/blog/([^/]+)", "dest": "/blog?slug=$1" } ] }
In this case, the Cache-Control
header will be applied to any route starting with /blog
.
status
Type: Integer
The status
property defines the status code that Now should respond with when a path is requested.
For example, you can use this property in combination with the headers
property to create a redirect with the initial status code of 301 (Moved Permanently).
{ "routes": [ { "src": "/about.html", "status": 301, "headers": { "Location": "/about-us.html" } } ] }
Redirecting one path to another using the status
property to provide a HTTP status code.
Location
property can also point to non-Now hosted URLs.Read more about redirecting your www.
subdomain to your root domain:
methods
Type: Array
The methods
property can be used to define what HTTP request methods a particular path accepts.
The value of this property can be any HTTP request method, with the default being that the path can accept any method.
As an example, you can use this property when you have an API endpoint and only want to allow GET
or POST
request methods:
{ "routes": [ { "src": "/api/user.js", "methods": ["POST", "GET"], "dest": "/api/user.js" } ], "builds": [{ "src": "*.js", "use": "@now/node" }] }
Accepting only POST
and GET
HTTP request methods on an API endpoint.
now.json
configuration that tells Now to build JavaScript files with Node.js and outputs them as Serverless Functions.Cascading Order
Routes are applied in the order they are listed in the routes
array. Take the following configuration; for example:
{ "routes": [ { "src": "/(.*)", "dest": "/" }, { "src": "/first-page", "dest": "/first-page.html" } ] }
An incorrect example now.json
file that will match allroutes
and proxy them to /
In the example configuration above, since the first route matches all possible paths, the second route will not be used. The order of these routes would have to switch for the latter route to apply to the /first-page
path.
The correct configuration for all routes to take affect would be the following:
{ "routes": [ { "src": "/first-page", "dest": "/first-page.html" }, { "src": "/(.*)", "dest": "/" } ] }
A correct example now.json
file that will match allroutes
, only proxying to /
if there are no matches.
This type of configuration can be seen in single-page applications where custom paths need to route to the index.html
file.
Route Parameters
Using PCRE Named Subpatterns, or capture groups, you can capture part of a path and use it in either the dest
or headers
properties.
Using route parameters enables you to change the format of your URLs easily without needing complicated routing code.
For example, if you are using URL parameters but want to use a custom URL path you can use the following:
{ "routes": [{ "src": "/product/(?<id>[^/]+)", "dest": "/product?id=$id" }] }
Using a URL parameter in src
and proxying it as a custom URL path in dest
.
This will take a URL, like /product/532004
and proxies it to /product?id=532004
with the user seeing your custom URL in their browser.
^
, asserting the start of the path string, and $
, asserting the end of the path string, are implied and are not necessary to write.As another example, if you want to redirect from all paths under a certain directory but want to keep the path in the new location, you can use the following:
{ "routes": [ { "src": "/posts/(.*)", "status": 301, "headers": { "Location": "/blog/$1" } } ] }
Redirecting from all paths in the posts
directory but keeping the path in the new location.
If you are using a Next.js app and want to learn more about using custom routes with ZEIT Now, read our guide:
Wildcard Routes
Sometimes, you will have wildcard routes that overlap with other routes. For example,
{ "routes": [ { "src": "/about" }, { "src": "/contact" }, { "src": "/([^/]+)", "dest": "/blog?slug=$1" } ] }
A now.json
file where filesystem routes are explicitly defined.
You might find that there are many routes without a dest
. These routes can be handled without being explicitly defined by using handle filesystem. Handle filesystem works the same as if you hardcoded all the routes in its place.
{ "routes": [ { "handle": "filesystem" }, { "src": "/([^/]+)", "dest": "/blog?slug=$1" } ] }
A now.json
file, using handle filesystem to route to filesystem routes.
In this example, handle filesystem expands to route /about
and /contact
.
Advanced Routing
This section intends to cover a number of advanced uses for routes, providing examples that can be adapted for use in your project.
Custom 404
The routes
array is processed before attempting a match. For example, this allows you to send a 404
status code for specific route patterns:
{ "version": 2, "routes": [{ "src": "/build/stats", "status": 404, "dest": "/404" }] }
A now.json
file, setting a 404
status code for the /build/stats
route.
This would return a 404
status code for the /build/stats
route.
To provide a custom 404 route, you can rely on the filesystem, providing a custom error page if there are no matches. The code below shows an example of how to do this:
{ "version": 2, "routes": [ { "handle": "filesystem" }, { "src": "/(.*)", "status": 404, "dest": "/404" } ] }
A now.json
file, setting a 404
status code for any routes not matched in the filesystem.
In the snippet above, precedence is given to the filesystem, routing only to /404
if there is no match.
SPA Fallback
A common pattern with single-page applications (SPAs) is to route everything towards a single file with the application parsing the path to handle the routing itself. Most SPAs have assets to serve as well, so you should handle the filesystem before rewriting the path:
{ "version": 2, "routes": [ { "handle": "filesystem" }, { "src": "/.*", "dest": "/index.html" } ] }
A now.json
file, providing a fallback to index.html
after checking for matches against filesystem routes.
In the example above, routing will take place based on the filesystem first, allowing for correct routing of assets and Serverless Functions, before falling back to the index.html
file.
Intercepting Routes
Routes can be intercepted by placing them either before or after "handle": "filesystem"
, this can be used to change the behavior of how your routes work.
For example, say the directory structure of a user's project looks like this:
project/ about.html contact.html blog.js index.html
An example project directory structure.
If you want to create a route that does not interfere with the other files in the filesystem, you should add "handle": "filesystem"
before:
{ "version": 2, "routes": [ { "handle": "filesystem" }, { "src": "/(?<slug>[^/]+)", "dest": "/blog?slug=$slug" } ] }
A now.json
file, providing a /blog
route after checking for matches against filesystem routes.
This is the equivalent of writing the routes below except when you add a new file, you don't need to change any config:
{ "version": 2, "routes": [ { "src": "/about.html", "dest": "/about.html" }, { "src": "/index.html", "dest": "/index.html" }, { "src": "/contact.html", "dest": "/contact.html" }, { "src": "/secret.html", "dest": "/secret.html" }, { "src": "/(?<slug>[^/]+)", "dest": "/blog?slug=$slug" } ] }
A now.json
file, expanded to show the effect of using the filesystem to handle routing.
If you want to append headers to block some of those paths, you need a route that comes before "handle": "filesystem"
:
{ "version": 2, "routes": [ { "src": "/about.html", "headers": { "Cache-Control": "max-age=600" }, "continue": true }, { "src": "/secret.html", "status": 404, "dest": "/404" }, { "handle": "filesystem" }, { "src": "/(?<slug>[^/]+)", "dest": "/blog?slug=$slug" } ] }
A now.json
file that will continue matching routes after /about.html
is matched.
By using "continue": true
, routes will continue to be matched, rather than stopping at the first match.
Using continue
There are some cases where you might want to continue routing after making a match. This is useful when scoping URLs or appending headers.
Global Headers with continue
A common usage for "continue": true
is to add global headers to routes, this is shown in the example:
{ "version": 2, "routes": [ { "src": "/.*", "headers": { "Cache-Control": "max-age=3600" }, "continue": true }, { "src": "/blog.*", "headers": { "Cache-Control": "max-age=600" }, "continue": true }, { "src": "/blog/([^/]+)", "dest": "/post?slug=$1" } ] }
A now.json
file using continue
to add global headers.
In this example, /test
would be cached for 3600
, /blog/whatever
would be cached for 600
. It's important to note when using continue
and headers, if a header is already set, it is overridden.
dest
and continue
When you need to rewrite the URL without matching, you should use "continue": true
.
{ "version": 2, "routes": [ { "src": "/test", "headers": { "Cache-Control": "max-age: 600" }, "continue": true }, { "src": "/(.*)", "dest": "/src/public/$1", "continue": true }, { "src": "/src/public/test", "dest": "/src/function/test" } ] }
A now.json
file using continue
to rewrite a URL without matching.
In this example, when requesting /test
, it will look for a response at /src/public/test
. Any routes that follow the dest
+ continue
route will match against the new path. For example, /test
would match all 3 routes.
Common Regex
When using the .
character in a PCRE Regex, you should keep in mind that in regex, it matches anything. A literal .
is denoted \.
. Slashes do not need to be escaped because the regular expression is enclosed in quotes.
Incorrect:
{ "src": "\/test\/file.json", "status": 404, "dest": "/404" }
An incorrect example of a now.json
route.
Considering this example, /test/file.json
would match, but /test/file-json
would too. This is not intended. The \/
slashes are distracting and needless.
Correct:
{ "src": "/test/file\.json", "status": 404, "dest": "/404" }
A correct example of a now.json
route.
Catch-All Except
A common pattern for URL slugs is catch-all except:
{ "src": "/blog/([^/]+)", "dest": "/blog?post=$1" }
A now.json
route using a catch-all except regex.
Anything that is not a /
will match in $1
. This allows /blog/post
to work, while not allowing /blog/post/edit
.
Negative Lookahead
When using catch-alls, it might make sense to exclude certain patterns instead of just a character. A negative lookahead might be useful with a directory structure similar to this:
project/ blog/ index.html post.html www/ index.html about.html
An example project directory structure.
In order to make /
use www/index.html
, you have to create a route:
{ "version": 2, "routes": [{ "src": "/(?!blog/?)(.*)", "dest": "/www/$1", "continue": true }] }
A now.json
route using a negative lookahead.
(?!blog/?)
is a negative lookahead and ensures the route doesn't start with /blog/
.
Limits
There is a limit of 256 route objects within a routes
array. If there are more than this limit, the deployment will fail.