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.
Caching
Add caching to a route using the
caching
feature:JavaScript
1router.get('/some/path', {2 caching: {3 max_age: '1d',4 stale_while_revalidate: '',5 service_worker_max_age: '1h',6 bypass_client_cache: true,7 },8 headers: {9 set_response_headers: {10 'x-sw-cache-control': 'max-age=3600',11 },12 },13});
Customizing the Cache Key
A cache key is automatically generated for each request, but if your web application relies on query string parameter(s), request header(s), or cookie(s) when generating a response, then you should customize the cache key to include those elements. You can customize the cache key using the
cache_key
feature:JavaScript
1router.get('/some/path', {2 caching: {3 max_age: '1d',4 cache_key: {5 // query string options are mutually exclusive; only one can be used for the cache key.6 exclude_all_query_params: boolean,7 include_all_query_params: boolean,8 include_all_query_params_except: ['session_id', 'utm_source'],9 include_query_params: ['page', 'filters'],1011 include_headers: ['x-my-header'],12 include_cookies: ['x-my-cookie', 'language', 'currency'],13 },14 },15});
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);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
Render the result of a Cloud Function within your application by using the
renderWithApp
method. Use this method to respond with an SSR or API result from your application.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('/some/:path*', ({addFeatures, renderWithApp}) => {2 addFeatures({3 caching: {4 max_age: '1d',5 bypass_client_cache: true,6 },7 });89 renderWithApp();10});
Image Optimization
Edgio can dynamically transform your images to tailor your site’s design, experience, and performance needs. Image optimization can be enabled using the
response.optimize_images
feature on your route(s).Optimizing Local Images
Images local to your project, such as those in your
public
directory, can be both served as static assets and processed by the image optimizer.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
1// match all /images/* requests2router.match(/\/images\/(.*)/, ({addFeatures, serveStatic}) => {3 // serve the image as a static asset, referencing the capture group from the match4 serveStatic('public/images/$1');56 // add the image optimization and caching feature7 addFeatures({8 caching: {9 max_age: '1d',10 },11 response: {12 optimize_images: true,13 },14 });15});
This example:
- Matches all
/images/*
requests - Proxies the request to the static asset origin where the asset is hosted
- Enables the image optimization feature
- Caches the response for 1 day
Sample request:
https://example.com/images/my-image.jpg?width=200&height=200
Rules should match using a regular expression that captures the image path and query string parameters containing the image optimization options. This is necessary to ensure optimization options are captured and passed to the image optimizer. Using simple path matching (e.g.
/images/:path*
) will not capture the query string parameters and optimizations will not be applied.Optimizing Remote Images
Images hosted on a remote server can be optimized by proxying the request to the origin and adding the image optimization feature.
JavaScript
1router.match('/images/:path*', {2 caching: {3 max_age: '1d',4 },5 origin: {6 set_origin: 'origin',7 },8 response: {9 optimize_images: true,10 },11});
This example:
- Matches all
/images/*
requests - Proxies the request to the
origin
origin where the asset is hosted - Enables the image optimization feature
- Caches the response for 1 day
If you need to modify the request path before proxying to the origin, you can use the
url.url_rewrite
feature.JavaScript
1router.match(/\/images\/(.*)/, {2 caching: {3 max_age: '1d',4 },5 origin: {6 set_origin: 'media',7 },8 url: {9 url_rewrite: [10 {11 source: '/images/(.*)',12 syntax: 'regexp',13 destination: '/assets/images/$1',14 },15 ],16 },17 response: {18 optimize_images: true,19 },20});
This example:
- Matches all
/images/*
requests - Proxies the request to the
media
origin where the asset is hosted - Rewrites the request path to
/assets/images/*
to match the path on the origin - Enables the image optimization feature
- Caches the response for 1 day
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, you must also set response.set_done
to true
. This will allow the request to continue matching any subsequent rules, but will prevent the request from being forwarded to the origin.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);
Redirecting HTTP to HTTPS
To redirect all HTTP traffic to HTTPS, use the
url.url_redirect
feature. Matching the source
with (.*)
will capture the entire path and query string that is then appended to the destination
. Referencing %{host}
in the destination
will ensure that the request is redirected to the current host. See Feature Variables for more information.JavaScript
1router.match(2 {scheme: 'HTTP'},3 {4 url: {5 url_redirect: {6 code: 302,7 source: '(.*)',8 destination: 'https://%{host}$1',9 },10 },11 }12);
Blocking Unwanted Traffic
Although there are various strategies for blocking unwanted traffic, the recommended method for blocking traffic is through Edgio Security’s access rules. Access rules allow you to define traffic profiles (e.g., country, IP address, or user agent) that should always be allowed, blocked, or undergo additional security screening.
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(4 or(5 {6 edgeControlCriteria: {7 '===': [8 {9 location: 'country',10 },11 'XX',12 ],13 },14 },15 {16 edgeControlCriteria: {17 '===': [18 {19 location: 'country',20 },21 'XY',22 ],23 },24 },25 {26 edgeControlCriteria: {27 '===': [28 {29 location: 'country',30 },31 'XZ',32 ],33 },34 }35 ),36 {37 access: {38 deny_access: true,39 },40 }41);
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 edge and permalinks, 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.
Token Auth
Token Auth provides a safeguard against hotlinking by requiring requests to contain a token value that defines the criteria that the request must satisfy before it can be served through our network.
Set up Token Auth by performing the following steps:
-
Define a primary encryption key.
-
Ensure that requests for content that will be protected by Token Auth contain a query string that start with a token.This step requires generating encrypted tokens that define the minimum access requirements. For example, you could use a server-side script to generate and inject tokens within links to protected content.
-
Enable Token Auth on the desired requests by adding the Token Auth feature to one or more rule(s).JavaScript1router.match(/^\/secure\/.+/i, {2 access: {3 token_auth: true4 }5});
-
Deploy your changes.