Configure and build Edge Handlers

This feature is in early access BETA.

This page will help you get started with Edge Handlers. You can visit our sign-up form to request an invitation to the early access beta program or go to our Forums to join the conversation about Edge Handlers.

# Component overview

Edge Handler behaviors are defined in files you store in your site repository. Each Edge Handler has two required component parts.

To create an Edge Handler to deploy with your site, you’ll confirm the default Edge Handlers directory or set a custom one, write dynamic routing logic in a handler file, and declare the Edge Handler in your site’s Netlify configuration file.

# Choose an Edge Handlers directory

At deploy time, Netlify’s build bots detect your handlers by checking for a netlify/edge-handlers directory in the site’s base directory in your repository.

If you want to customize the Edge Handlers directory name or location, you can do so in the Netlify configuration file in the [build] settings. The path is either relative to the root of the repository, or, if a base directory is set, it’s relative to the base directory.

edge_handlers = "src/my-edge-handlers"

Handler files can’t be organized into subdirectories within your Edge Handlers directory.

# Build logic in handler files

Netlify supports Edge Handlers written in JavaScript. All handlers have a similar structure, and you can use them to intercept or manipulate an incoming HTTP request or to transform an HTTP response.

# File names

Handler file names are the basis for identifiers that you use when declaring your Edge Handlers. For example, a handler in logRequests.js will be identified as logRequests while a handler in filter-requests.js will be identified as filter-requests.

# Edge Handler syntax

All Edge Handlers have the same function signature. Handlers export a function named onRequest that receives an event object with information about the request that the handler is processing. Edge Handlers fully support the ES2020 specification, so features like template literals, BigInt, and async/await are supported natively without having to use a JavaScript compiler like Babel.

Here’s an example implementation:

// Log every incoming request URL
export function onRequest(event) {
  console.log(`Incoming request for ${event.requestMeta.url}`);

In the sample code above, the console logs each incoming request URL, and then requests execute normally to serve any assets associated with the request path.

# Modify requests and responses

Edge Handlers allow you to intercept incoming HTTP requests, manipulate incoming requests to fetch different resources, and transform HTTP responses.

# Intercept HTTP requests

Netlify Edge Handlers allow your site to intercept incoming HTTP requests and serve different content than what our edge network would serve for a request that wasn’t intercepted. This feature is useful if you want to prevent visitors from accessing specific content but you still want to give them something.

Here’s an example of intercepting a request:

export function onRequest(event) {
  if (event.requestMeta.url.pathname === "/secret") {
      new Response("<h1>Access denied</h1>", {
        headers: {
          "content-type": "text/html",
        status: 404,

The replaceResponse method is flexible depending on your needs.

  • replaceResponse can accept a Response directly to immediately send it to the client. The below code sample illustrates this.

    export function onRequest(event) {
        new Response(null, {
          status: 301,
          headers: {
            Location: "",
  • replaceResponse can take a callback as an argument. The callback can be synchronous or asynchronous and produces the response that is sent to the client. Inside the callback, the handler has access to the incoming request and its body, as they are streamed from the client. The below code sample illustrates this.

    export function onRequest(event) {
      event.replaceResponse(({ request }) => {
        const url = new URL(request.url);
        url.pathname = `/api/v1/${url.pathname}`;
        return fetch(url, { body: request.body });

# Manipulate HTTP requests

Requests received by an Edge Handler can be manipulated to fetch different resources than the ones our edge network would serve by default. This feature allows changing all aspects of the request including query parameters, request paths, headers, the HTTP method, request body, and protocol.

Here is an example of manipulating a request:

export function onRequest(event) {
  const { url, headers } = event.requestMeta;
  if (
    url.pathname === "/secret" &&
  ) {
    url.pathname = "/login";
    event.replaceResponse(() => fetch(url.href));

In this example, if an incoming request to /secret doesn’t include authorization information, the response points to /login.

# Transform HTTP responses

You can also use Edge Handlers to transform default responses sent from the edge network back to the browser in response to incoming requests.

Here’s an example:

let encoder = new TextEncoder();

export function onRequest(event) {
  event.replaceResponse(async ({ request }) => {
    let originResponse = await fetch(request);
    let { readable, writable } = new TransformStream();

    // forward the data in the background as it’s being read by the browser
    (async () => {
      let writer = writable.getWriter();

      try {
        for await (let chunk of originResponse.body) {
          await writer.write(chunk);
        await writer.write(
          encoder.encode("<p>Served from a Netlify Edge Handler</p>")
      } finally {
        await writer.close();

    return new Response(readable, {
      headers: { "Content-Type": "text/html" },

In this example, the original response is transformed to an updated response body that includes Served from a Netlify Edge Handler.

# Helper objects

The Edge Handlers runtime environment includes the following objects to help you work with handlers.

# Console

The console object enables logging information about Edge Handlers as they execute. You can inspect the output of these calls in the Edge Handler logs in the Netlify UI.

These exported console methods are available:

  • assert()
  • debug()
  • error()
  • info()
  • log()
  • warn()

Here’s an example using a console method within an Edge Handler:

export function onRequest(event) {
  console.log(`incoming request for ${event.requestMeta.url.pathname}`);


The URL object enables working with resource locations. This object is compatible with the core JavaScript URL object.

Here’s an example using the URL object to create a new URL to fetch content from:

export function onRequest(event) {
  let url = new URL("");
  url.pathname = "/emojis";
  event.replaceResponse(() => fetch(url.href));

# URLSearchParams

The URLSearchParams object holds search parameters key/value pairs. This object uses the application/x-www-form-urlencoded format to serialize the query parameters as described in the URL standard.

# API resources

For more information about writing Edge Handler logic, check out the following resources:

# Manage dependencies

Specify any Edge Handler dependencies in the top-level package.json file in your site’s base directory.

All dependencies within Edge Handler code must be installed locally and then bundled inside the Edge Handler before the artifact is deployed to our edge nodes. Dynamic import statements for dependencies aren’t allowed. You can’t include dependencies at run time, and your code must be completely isolated before Netlify runs it. This helps keep deploys atomic and dependencies synchronized.

Consider this example Edge Handler, content-updated.js:

import { formatDistance, subDays } from "date-fns";

export function onRequest(event) {
  const timePassed = formatDistance(subDays(new Date(), 3), new Date());
  const html = `
        <title>New content</title>
        <h1>Content updated: ${timePassed} ago</h1>
  event.replaceResponse(new Response(html));

If you don’t install date-fns as a dependency, the build will fail with the error message Error in edge-handlers/content-updated.js, could not resolve date-fns module. Please install this dependency locally and ensure it is listed in your package.json.

You can install the dependency by listing it directly in package.json or by running a command in your project’s base directory.

  "name": "my-site",
  "version": "1.0.0",
  "dependencies": {
    "date-fns": "^2.19.0"
npm install --save date-fns
yarn add date-fns

# Declare Edge Handlers in netlify.toml

Before our CDN edge nodes can use your handler logic to filter or alter requests, you must declare your Edge Handlers in the Netlify configuration file.

In netlify.toml, use TOML’s array of tables to declare Edge Handlers. For each declaration, add a separate [[edge_handlers]] entry to specify a path and the handler to execute on requests to that path.

# On requests to `/some-path`, execute `log` handler
  path = "/some-path"
  handler = "log"

# On requests to `/spain/some-path`, execute `logInSpanish` handler
  path = "/spain/some-path"
  handler = "logInSpanish"

# Execute `filterRequests` handler for any browser request
# to the site except for requests that already matched a
# handler declaration above
  path = "/*"
  handler = "filterRequests"

Edge Handler identifiers are based on handler file names. For example, a handler in myHandler.js is identified as myHandler. Each Edge Handler declaration can contain one handler identifier only.

When specifying a path, you can use an asterisk as a wildcard that matches anything that follows it.

# Declaration processing order

Keep the following considerations in mind when working with Edge Handlers.

  • First match executes. For each request, the routing engine processes the first matching Edge Handler declaration it finds in netlify.toml. The file is evaluated from top to bottom.

      path = "/cats"
      handler = "logCats"
    # The `filterCats` handler never executes because the
    # previous handler matches requests to `/cats` first
      path = "/cats"
      handler = "filterCats"
  • Trailing slash ignored. Our CDN edge nodes do URL normalization before processing Edge Handlers. What this means for your Edge Handler declarations is that Netlify will match paths regardless of whether or not they contain a trailing slash.

      path = "/my-path"
      handler = "myHandler"
    # The `myOtherHandler` Edge Handler below never executes because
    # requests to `/my-path/` match the above declaration for `/my-path`
      path = "/my-path/"
      handler = "myOtherHandler"
  • Edge Handlers run before redirects. For each request, Edge Handlers are processed before redirect rules.

      path = "/old-path"
      handler = "logRequest"
      from = "/old-path"
      to = "/new-path"

    In the above example, a request to /old-path executes the logRequest handler before being redirected to /new-path.

  • Redirect status code matters. The default status code for redirects is 301. This results in a new request to the target path which the routing engine evaluates for Edge Handler matches starting at the top of netlify.toml.

      path = "/new-path"
      handler = "filterRequest"
      from = "/old-path"
      to = "/new-path"

    In the above example, a request to /old-path is redirected to /new-path which will execute the filterRequest handler.

    Redirects with status code 200 are rewrites that fetch content from the target path without changing the request URL. Because of this, any Edge Handler declared for the target path of a rewrite will not execute for rewritten requests.

      path = "/dogs"
      handler = "filterDogs"
      from = "/best-pets"
      to = "/dogs"
      status = 200

    In the above example, a visitor to /best-pets would receive a response from /dogs without changing the URL in their browser address bar or executing the filterDogs handler.

# Local development

You can develop and test Edge Handlers locally before deploying them to Netlify. To do so, install Netlify CLI and use Netlify Dev to start a development environment that executes your handlers on local requests.

Enter the following command from your project’s root directory to start the environment. (The --edgeHandlers flag is in early access.)

netlify dev --edgeHandlers

This command inspects your project for Edge Handler sources and reloads them whenever you make a modification.

# Geolocation

Netlify uses a commercial localization database to add visitor geolocation data to each handler request’s payload. This localization database is not provided in the development environment.

There are two options for working with and testing geolocation data in a development environment. You can use your own location database or inject Geolocation information.

# Use your own location database

You can download your own location database in MaxMind DB (MMDB) format and start a public live session to get an address for your local environment.

Enter the following command to launch a public live session with Edge Handler support. (The --edgeHandlers flag is in early access.)

netlify dev --edgeHandlers --live --locationDb /PATH_TO_FILE/ip-db.mmdb

The --live flag creates a unique subdomain for your local session under Your Edge Handler receives your public IP address once you use the URL to access your site. --locationDb specifies the path to the location database you downloaded earlier.

# Inject geolocation information

You can also inject the geolocation information you’d like to test directly into your request headers.

For example, if you want to test a handler’s behavior from a different country, you can set the X-NF-Country-Code header in a curl request like this:

curl -H 'X-NF-Country-Code: ES' http://localhost

If you want to modify the request headers that your browser sends to a local handler, use a browser extension like ModHeader.

You can find more information on the geolocation headers we inject into each request in the Edge Handlers API overview.

Deploy and monitor Edge Handlers

Deploy and monitor Edge Handlers: Bundle your code