Route features identify actions that will be applied to a request. Popular features include:
- Caching: Customizes when and how content is cached.
- Headers: Adds, modifies, or deletes headers from the request or response.
- Origin: Controls how the CDN communicates with an origin server.
- Response: Customizes the response sent to the client and determines whether we will allow prefetching instructions to be sent to the client.
- Set Variables: Assigns a value to one or more user-defined variable(s) that are passed to your bespoke traffic processing solution.
- URL: Redirects or rewrites requests to a different URL.
See Features Reference for a complete list of features and their behavior.
Defining Route Features
As outlined in the Route Features section of the CDN-as-Code guide:
- Route features are defined as the second argument to the
Router
method being called in theroutes.js
file, such as.match()
,.get()
,.post()
, etc. - May also be defined in conditional routes such as
.if()
,.elseif()
, etc.
The argument is an object that supports features outlined in the Features Reference. The following example shows how to define a route feature that proxies a request to the origin host and caches it for 1 hour:
JavaScript
1router.match('/:path*', {2 caching: {3 max_age: '1h',4 },5 origin: {6 set_origin: 'origin',7 },8});
Route features are often defined using object notation, but in some cases, it may be necessary to use RouteHelper methods to define features. Some functionality such as transforming requests/responses or serving static files requires the use of
RouteHelper
methods.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.When you’re mixing usage of object notation and
RouteHelper
methods, the addFeatures()
function allows you to seamlessly integrate features defined in object notation directly within RouteHelper
instance. The examples below illustrate the two notation styles, and then show how to combine them:JavaScript
1// Using different notation styles2router3 // Define caching using object notation4 .get('/some-path', {5 caching: {6 max_age: '1h',7 },8 })910 // Serve a static file using a RouteHelper method11 .get('/some-path', ({serveStatic}) => {12 serveStatic('public/some-path.html');13 });1415/* or */1617// Combining notation styles18router.get('/some-path', ({addFeatures, serveStatic}) => {19 // Use `addFeatures` RouteHelper method to define caching using object notation20 addFeatures({21 caching: {22 max_age: '1h',23 },24 });2526 // Use `serveStatic` RouteHelper method to serve a static file27 serveStatic('public/some-path.html');28});
While both of these examples are functionally equivalent, the second example is more flexible because it allows you to define a single route using both notation styles for different features.
Common Routing Features
The following sections describe common routing features and how to use them.
Debug Cache Headers
The debug cache response headers provide additional information about the cache policy applied to the requested asset. Learn more.
To enable debug mode, you need to set the
debug_header
feature. In your routes.js
file, add the following:JavaScript
1import {Router, edgioRoutes} from '@edgio/core/router';23export default new Router().use(edgioRoutes).get('/some-path', {4 /* ... */5});
Including
edgioRoutes
in your router will automatically enable the debug feature for all routes. You can also enable debug mode for a specific route by adding the debug_header
feature to the route:JavaScript
1router.match('/some-path', {2 headers: {3 debug_header: true,4 },5 /* ... */6});
To see the debug headers in the response, you will need to specify the
x-ec-debug
header in your request. This request header should list the values of the debug headers you want to see in the response as defined under Requesting Debug Cache Information.For example, you can use the
edgio curl
command to request the x-ec-cache
and x-ec-cache-state
headers:Bash
1edgio curl https://your-site.com/some-path -H "x-ec-debug:x-ec-cache,x-ec-cache-state"
See an example of the response headers using our Simple Performance example site:
Bash
1# with edgio curl2edgio curl https://edgio-community-examples-v7-simple-performance-live.edgio.link/ -H "x-ec-debug:x-ec-cache,x-ec-check-cacheable,x-ec-cache-key,x-ec-cache-state,x-ec-cache-remote"34# with curl5curl -I -H "x-ec-debug: x-ec-cache,x-ec-check-cacheable,x-ec-cache-key,x-ec-cache-state,x-ec-cache-remote" https://edgio-community-examples-v7-simple-performance-live.edgio.link/
In the following output, you will see the debug headers that were available in the response:
Diff
1➜ ~ edgio curl https://edgio-community-examples-v7-simple-performance-live.edgio.link/ -H "x-ec-debug:x-ec-cache,x-ec-check-cacheable,x-ec-cache-key,x-ec-cache-state,x-ec-cache-remote"23URL : https://edgio-community-examples-v7-simple-performance-live.edgio.link/ 🔗4From: 192.168.50.150:51204 🖥️5To : 64.12.0.33:443 🌎67HTTP/2 2008Response Headers9 accept-ranges: bytes10 cache-control: public, max-age=0, must-revalidate11 content-length: 41711512 content-type: text/html; charset=UTF-813 date: Wed, 31 May 2023 14:50:30 GMT14 etag: "665423646ec1a20bb8e43f8a71a132ba-ssl"15 last-modified: Wed, 31 May 2023 14:36:48 GMT16 server: Netlify17 server-timing: edgio_cache;desc=TCP_EXPIRED_HIT,edgio_pop;desc=dcd,edgio_country;desc=US18 strict-transport-security: max-age=3153600019+ x-ec-cache: TCP_EXPIRED_HIT from ECAcc (dcd/7D26)20+ x-ec-cache-key: //http/801B1A5C/origin/53/:/hs-471828820914581765921+ x-ec-cache-state: max-age=0 (0s); must-revalidate; cache-ts=1685544630 (Wed, 31 May 2023 14:50:30 GMT); cache-age=0 (0s); remaining-ttl=0 (0s); expires-delta=none22+ x-ec-check-cacheable: YES23 x-edg-mr: 7:3;24 x-edg-version: 53 7 4 7.0.7 2023-05-31T14:32:16Z be36ceec-4cfd-4d56-aa41-ced08cd5352c25 x-nf-request-id: 01H1S4KY3H6QM7BZ0MRJC2C3MQ262728 DNS Lookup TCP Connection TLS Handshake Server Processing Content Transfer29[ 5ms | 204ms | 69ms | 87ms | 85ms ]30 | | | | |31 namelookup:5ms | | | |32 connect:209ms | | |33 pretransfer:278ms | |34 starttransfer:365ms |35 total:450ms3637Response Body38 Disabled. To enable use EDGIO_CURL_SAVE_BODY=true or EDGIO_CURL_SHOW_BODY=true3940➜ ~
Proxying an Origin
Same Path
To forward a request to the same path on one of the origins listed in
edgio.config.js
, use the origin
feature:JavaScript
1router.get('/some-path', {2 origin: {3 set_origin: 'origin',4 },5});
The value of
set_origin
corresponds to the name
property of an origin in edgio.config.js
. For example:JavaScript
1module.exports = {2 // ... other configurations34 origins: [5 {6 name: 'origin',7 hosts: [8 {9 // The domain name or IP address of the origin server10 location: 'www.my-site.com',11 },12 ],13 },14 ],15};
Different Path
To forward the request to a different path, use the
url.url_rewrite
feature:JavaScript
1router.get('/products/:productId', {2 origin: {3 set_origin: 'origin',4 },5 url: {6 url_rewrite: [7 {8 source: '/products/:productId',9 syntax: 'path-to-regexp',10 destination: '/p/:productId',11 },12 ],13 },14});
Adding Caching
To cache proxied requests at the edge, use the
caching
feature:JavaScript
1router.get('/products/:productId', {2 caching: {3 max_age: '1d',4 stale_while_revalidate: '1h',5 },6 origin: {7 set_origin: 'origin',8 },9});
Altering the Request
You can alter request headers when forwarding a request to a backend:
JavaScript
1router.get('/products/:productId', {2 headers: {3 set_request_headers: {4 'header-name': 'some-value',5 },6 },7 origin: {8 set_origin: 'origin',9 },10});
The above example makes use of the
headers.set_request_headers
feature.Altering the Response
You can also alter the response before and after the cache:
JavaScript
1router.get('/products/:productId', {2 origin: {3 set_origin: 'origin',4 },5 headers: {6 // remove `header-name` from the origin response7 remove_origin_response_headers: ['header-name'],89 // add `header-name` to the response, appending the value to the10 // header if it already exists11 add_response_headers: {12 'header-name': 'some-value',13 },1415 // set/overwrite `header-name` to `some-value` in the response16 set_response_headers: {17 'header-name': 'some-value',18 },1920 // append `another-value` to `header-name` in the response,21 // becoming `some-value,another-value`22 set_response_headers: {23 '+header-name': ',another-value',24 },2526 // remove `header-name` from the response by name27 remove_response_headers: ['header-name'],2829 // or set with an empty value to remove `header-name` from the response30 set_response_headers: {31 'header-name': '',32 },33 },34});
Additional information on the
headers
feature can be found in the Features guide.Altering All Responses
You can also write catch-all routes that will alter all responses. One example where this is useful is injecting Content Security Policy headers.
Another example is adding response headers for debugging, which is often useful if Edgio is behind another CDN or if you are troubleshooting your router rules. For example, you could respond with the value of request
%{http_x_forwarded_for}
into x-debug-xff
to see the value that Edgio is receiving from the CDN:JavaScript
1router.match(2 {3 path: '/:path*',4 query: {5 my_site_debug: 'true',6 },7 },8 {9 headers: {10 set_response_headers: {11 'x-debug-xff': '%{http_x_forwarded_for}',12 },13 },14 }15);
The rules for interpolating the values of request and response objects can be found in the routing guide.
Note that catch-all routes that alter headers, cookies, or caching can be placed at the start of your router while allowing subsequent routes to run because they alter the request or the response without actually sending a response. See route execution for more information on route execution order and sending responses.
Transforming Requests / Responses
If you need to modify a request before going to an origin, or modify the response from an origin, you may use
transformRequest
and transformResponse
functions on the proxy
handler. Transform functions will be executed within the Edgio cloud, and will not be executed on the edge. See Cloud Functions for more information.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.transformRequest Function
You can modify the request before it is sent to the origin using the
transformRequest
function. This example shows how you could add a foo
property to the request body before sending it to the origin:JavaScript
1router.get('/products/:productId', ({proxy}) => {2 proxy('origin', {3 transformRequest: (request) => {4 request.body = JSON.stringify({5 ...JSON.parse(request.body),6 foo: 'bar',7 });8 },9 });10});
transformResponse Function
Similarly, you can modify the response from the origin before it is sent to the client using the
transformResponse
function. This example shows how you could add an HTML script
tag to the response body before sending it to the client:JavaScript
1import responseBodyToString from '@edgio/core/utils/responseBodyToString';2import $ from 'cheerio';3/* ... */4router.get('/products/:productId', ({proxy}) => {5 proxy('origin', {6 transformResponse: (response) => {7 const body = responseBodyToString(response.body);8 const $body = $(body).append(9 '<script src="https://example.com/script.js"></script>'10 );11 response.body = $body.html();12 },13 });14});
Manipulating Cookies
You can manipulate cookies before they are sent to the browser using
headers.set_response_headers
:JavaScript
1router.get('/products/:productId', {2 origin: {3 set_origin: 'origin',4 },5 headers: {6 // add cookie `foo=bar` to the response7 add_response_headers: {8 'set-cookie': 'foo=bar;',9 },10 },11});
Adding Options to Cookies
In addition to the name and value of the cookie, you can also add attributes to each cookie. For
information on possible cookie attributes, please refer to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
JavaScript
1import {serializeCookie} from '@edgio/core/utils/cookieUtils';23router.get('/products/:productId', {4 origin: {5 set_origin: 'origin',6 },7 headers: {8 // set cookie with options9 add_response_headers: {10 'set-cookie': 'foo=bar; Secure; HttpOnly;',11 },1213 // set cookie with options using the serializeCookie helper14 add_response_headers: {15 'set-cookie': serializeCookie('foo', 'bar', {16 Secure: true,17 HttpOnly: true,18 }),19 },20 },21});
Proxying to Different Backends Based on Different Host Names
To proxy to different backends by matching the
host
header (e.g. different backends for different international sites):JavaScript
1router2 .match(3 {4 path: '/:path*',5 headers: {6 host: 'yoursite.c1',7 },8 },9 {10 origin: {11 set_origin: 'country1-backend',12 },13 }14 )15 .match(16 {17 path: '/:path*',18 headers: {19 host: 'yoursite.c2',20 },21 },22 {23 origin: {24 set_origin: 'country2-backend',25 },26 }27 )28 .match(29 {30 path: '/:path*',31 },32 {33 origin: {34 set_origin: 'everybody-else-backend',35 },36 }37 );
Serving a Static File
To serve a specific file, use the
serveStatic
method.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.JavaScript
1router2 // cache the favicon for 1 day3 .get('/favicon.ico', {4 caching: {5 max_age: '1d',6 client_max_age: '1h',7 },8 })910 // serve the favicon from the `public` directory11 .get('/favicon.ico', ({serveStatic}) => serveStatic('public/favicon.ico'));
Serving Static Files From a Directory
To serve a files from a directory, use the
serveStatic
method.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.JavaScript
1router2 // cache the static assets for 1 day3 .get('/assets/:path*', {4 caching: {5 max_age: '1d',6 client_max_age: '1h',7 },8 })910 // serve the assets from the `public` directory11 .get('/assets/:path*', ({serveStatic}) => serveStatic('public/:path*'));
Serving the Service Worker
Similar to the above example, you can serve the service worker from its directory (e.g.
/dist/service-worker.js
).This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.JavaScript
1router2 // cache the service worker for 1 day3 .get('/service-worker.js', {4 caching: {5 max_age: '1d',6 client_max_age: '1h',7 },8 })910 // serve the service worker from the `dist` directory11 .get('/service-worker.js', ({serveStatic}) =>12 serveStatic('dist/service-worker.js')13 );
Routing to Cloud Functions
If your request needs to be run on the Edgio cloud, you can use the
SERVERLESS_ORIGIN_NAME
origin to render your result using your application. Use this method to respond with an SSR or API result from your application:JavaScript
1import {SERVERLESS_ORIGIN_NAME} from '@edgio/core/origins';23router.get('/some/:path*', {4 caching: {5 max_age: '1d',6 bypass_client_cache: true,7 },8 origin: {9 set_origin: SERVERLESS_ORIGIN_NAME,10 },11});
Responding with a String Response Body
To respond with a simple, constant string as the response body use the
response.set_response_body
and response.set_done
features:JavaScript
1router.get('/some-path', {2 caching: {3 max_age: '1d',4 },5 headers: {6 set_response_headers: {7 'Content-Type': 'text/html',8 },9 },10 response: {11 set_done: true,12 set_response_body: `13 <!doctype html>14 <html>15 <body>Hello World</body>16 </html>`,17 },18});
When using
response.set_response_body
to send a response, or to stop processing a request from potentially matching subsequent routes, you must also set response.set_done
to true
.To compute a dynamic response, use the
compute
method.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.JavaScript
1router.get('/hello/:name', ({cache, setResponseHeader, compute, send}) => {2 cache({3 edge: {4 maxAgeSeconds: 60 * 60 * 24, // cache for 24 hours5 },6 });7 setResponseHeader('Content-Type', 'text/html');8 compute((request, response) => {9 send(`10 <!doctype html>11 <html>12 <body>Hello ${request.params.name}</body>13 </html>14 `);15 });16});
Redirecting
To redirect the browser to a different URL, use the
url.url_redirect
feature, optionally specifying the HTTP status code:JavaScript
1router.get('/p/:productId', {2 url: {3 url_redirect: {4 code: 301,5 source: '/p/:productId',6 syntax: 'path-to-regexp',7 destination: '/products/:productId',8 },9 },10});
To compute the destination URL, use the
compute
method.This feature requires a
RouteHelper
class method, since it cannot be implemented through standard object-based configuration. However, we strongly recommend to only use RouteHelper
for documented use cases.JavaScript
1router.get('/p/:productId', ({redirect, compute, cache}) => {2 cache({3 edge: {4 maxAgeSeconds: 60 * 60 * 24, // cache for 24 hours5 },6 });7 compute(async (request) => {8 const destination = await getDestinationFromMyAPI(request.params.productId);9 redirect(destination);10 });11});
Redirecting All Traffic to a Different Domain
JavaScript
1// Redirect all traffic except those with host header starting with www. to www.mydomain.com2router.match(3 {headers: {host: /^(?!www\.).*$/}},4 {5 url: {6 url_redirect: {7 code: 302,8 destination: 'https://www.mydomain.com%{request_uri}',9 },10 },11 }12);1314// Redirect all traffic from www.domain.com to domain.com15router.match(16 {headers: {host: /^(www\.).*$/}},17 {18 url: {19 url_redirect: {20 code: 302,21 destination: 'https://domain.com%{request_uri}',22 },23 },24 }25);
Blocking Unwanted Traffic
Blocking traffic from specific countries
If you need to block all traffic from a specific country or set of countries, you can do so by matching requests by the country code using the
location.country
match condition:JavaScript
1import {or} from '@edgio/core';23router.if(or(4 {5 edgeControlCriteria: {6 '===': [7 {8 location: 'country',9 },10 'XX',11 ]12 }13 },14 {15 edgeControlCriteria: {16 '===': [17 {18 location: 'country',19 },20 'XY',21 ]22 }23 },24 {25 edgeControlCriteria: {26 '===': [27 {28 location: 'country',29 },30 'XZ',31 ]32 }33 }),34 {35 access: {36 deny_access: true,37 },38 }39);
Learn more about geolocation headers in the Request guide. For detailed information on complex rules, see Conditional Routes.
Blocking Search Engine Crawlers
If you need to block all search engine bot traffic to specific environments (such as your default or staging environment), the easiest way is to include the
x-robots-tag
header with the same directives you would otherwise set in a meta
tag.The search engine traffic is automatically blocked on Edgio edge links and permalinks as of Edgio v6.
If you would like to enable indexing on those links, you need to pass
{ indexPermalink: true }
into the Router constructor in routes.js
file:JavaScript
1new Router({indexPermalink: true});
Otherwise, Edgio will match requests with the
host
header matching /edgio.link|edgio-perma.link/
and set a response header of x-robots-tag: noindex
.Additionally, you can customize this to block traffic to development or staging websites based on the
host
header of the request:JavaScript
1router.get(2 {3 headers: {4 // Regex to catch multiple hostnames5 host: /dev.example.com|staging.example.com/,6 },7 },8 {9 headers: {10 set_response_headers: {11 'x-robots-tag': 'noindex',12 },13 },14 }15);
For other available directives, see Google Developer Central and Bing Webmaster Tools for lists of supported options.