Rewrites and proxies

When you assign an HTTP status code of 200 to a redirect rule, it becomes a rewrite. This means that the URL in the visitor's address bar remains the same, while Netlify's servers fetch the new location behind the scenes.

This can be useful for single page apps, proxying to other services, or transitioning for legacy content.

Except where noted, the examples on this page use the _redirects file syntax, but all of these options are available in the Netlify configuration file syntax as well.


Rewrites can be very powerful, but there are a few things they cannot do:

  • For security reasons, rewrites between Netlify sites belonging to different teams are not allowed.
  • Infinitely looping rules (where the "from" and "to" resolve to the same location) are invalid and will be ignored.
  • By default, we limit internal rewrites to one "hop" — you cannot proxy from Netlify SiteA to Netlify SiteB to Netlify SiteC in a single request. This limitation may be amended for customers on a case-by-case basis. Contact support for more information.
  • Proxy rewrite requests will time out after 26 seconds. If you are proxying to a longer-running process, we recommend making an asynchronous request rather than waiting for a response.

History pushstate and single-page apps

If you're developing a single page app and want history pushstate to work so you get clean URLs, you'll want to enable the following rewrite rule:

/*    /index.html   200

This will effectively serve the index.html instead of giving a 404 no matter what URL the browser requests.


By default, you can't shadow a URL that actually exists within the site. This applies to rewrites using a splat or dynamic path segment as well as rewrites for individual routes or files. This means that even if you've setup the following rewrite rule:

/*   /index.html   200

The path /partials/chat.html would still render the contents of that file, if that file actually exists. This tends to be the preferred behavior when setting up rewrite rules for single page apps, etc.

However, if you're 100% sure that you'll always want to redirect, even when the URL matches a static file, you can append an exclamation mark to the rule:

/app/*  /app/index.html  200!

This will rewrite everything within /app/* to /app/index.html even if a file matches the URL.

This method can also be applied to individual routes or files:

/best-pets/dogs  /best-pets/cats.html 200!

With the rule above, /best-pets/dogs will always display the content in /best-pets/cats.html even if there is a file at /best-pets/dogs/index.html.

Proxy to another service

Just like you can rewrite paths like /* to /index.html, you can also set up rules to let parts of your site proxy to external services. Let's say you need to communicate from a single-page app with an API on that doesn't support CORS requests. The following rule will let you use /api/ from your JavaScript client:

/api/*  200

Now all requests to /api/... will be proxied through to straight from our CDN servers without an additional connection from the browser. If the API supports standard HTTP caching mechanisms like ETags or Last-Modified headers, the responses will even get cached by our CDN nodes.

Custom headers in proxy redirects

For redirect rules specified in the Netlify configuration file, you can add a map with custom headers for your proxy redirects, and Netlify will send them with every request:

  from = "/search"
  to = ""
  status = 200
  force = true
  headers = {X-From = "Netlify"}

Signed proxy redirects

You can use a JSON Web Signature (JWS) to sign all proxy requests to an external URL. To enable JWS on your requests, Netlify requires a secret token. You can set that token in your site’s environment variables, and reference it in the redirect rule.

Signed proxy redirects must be specified in the Netlify configuration file.

  from = "/search"
  to = ""
  status = 200
  force = true

The JSON document we sign with this JWS header has this format:

  "netlify_id": "the site's id in netlify",
  "site_url": "the site's URL for a given deploy",
  "context": "the deploy context for this request"