Routes
The routes list maps request paths onto the Functions you declared, narrows them by method, and sets response headers.
A route says which requests reach which Function. Static files never need one: the build registers every published asset itself, so routes is only for the paths your own code answers.
Declaring a route
version: 4
functions:
api/index.js:
runtime: node-22
memory: 512
routes:
- source: ^/api(/.*)?$
destination: api/index.js
methods: [GET, POST]
headers:
cache-control: no-store| Key | Type | Required | Notes |
|---|---|---|---|
source | string | yes | Matched against the request path without its query string |
destination | string | yes | The entrypoint path this route serves |
methods | list of methods | no | ANY, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. Defaults to [ANY] |
headers | map | no | Response headers added after the Function returns |
A config file holds at most 1024 routes, and source and destination are capped at 4096 characters each.
destination takes no leading slash
The build pairs a route with a Function by comparing destination to the entrypoint path as an exact string, and
entrypoint paths are project-relative: api/index.js, not /api/index.js. A destination written with a leading slash
passes validation, deploys without an error, and then answers nothing, because no route was ever created for it.
Writing a source
Three forms cover almost everything:
| Form | Example | Matches |
|---|---|---|
| A literal path | /healthz | That path, compared without regard to case |
A prefix with * | /* | Any path starting with the text before the * |
| A regular expression | ^/api(/.*)?$ | Any path the expression matches |
A source is tested as an expression when it starts with ^, and also when it starts with / and contains a dot. Nothing closes the expression for you: ^/api matches /apidocs and /api-internal, while ^/api(/.*)?$ matches neither. Matching order and the rest of the edge's resolution sequence are in Request routing.
Leaving methods out matches every method.
Common shapes
One server answering everything, which is what framework detection generates:
routes:
- source: /*
destination: dist/server.jsA JSON API on one Function and everything else on another, with the more specific pattern free to sit anywhere in the list:
routes:
- source: ^/api(/.*)?$
destination: api/index.js
- source: /*
destination: public/index.phpA webhook that only accepts POST, so a stray GET gets a 404 from the edge instead of an invocation:
routes:
- source: /webhooks/stripe
destination: api/webhooks.js
methods: [POST]The handler is decided for you
There is no key that picks how a route is served. The build derives it: a destination that names one of your entrypoints becomes a Function route, with the response headers you configured applied to it. Static files get their own routes for GET, HEAD, and OPTIONS at the same time, which is why they never appear in this list. Assets covers what the build publishes.
Keys the platform does not act on
The v4 schema accepts four more route keys, and one more form of destination, that the deployment never turns into an edge route:
redirectandstatusCode, for returning a redirect or overriding the status.hasandmissing, for requiring or forbidding a host, header, cookie, or query match.- A
destinationthat is an externalhttp://orhttps://URL, for proxying.
A route is created only for a destination that names one of your entrypoints, and only the path, the methods, and the response headers carry across. Nothing else survives. A route that adds statusCode, has, or missing still deploys, and behaves as though you had not written them. A redirect, or an external destination, produces no route at all, so that path answers 404. Serve it from a Function instead.
