Netlify redirects and rewrites don’t just move users; they fundamentally alter how Netlify interprets and serves your files.
Let’s see it in action. Imagine you have a static site with a SPA (Single Page Application) routing. Your index.html is at the root, but your app handles routes like /about or /users/123. Without proper configuration, a direct request for /about will result in a 404 because Netlify won’t find an about.html file.
Here’s a _redirects file in your project’s public directory:
# Redirects and Rewrites
/old-page /new-page 301
/about /index.html 200
/users/:id /index.html?userId=:id 200
And here’s the equivalent in netlify.toml:
[[redirects]]
from = "/old-page"
to = "/new-page"
status = 301
[[redirects]]
from = "/about"
to = "/index.html"
status = 200
[[redirects]]
from = "/users/:id"
to = "/index.html?userId=:id"
status = 200
When a user requests /about, Netlify matches the second rule. It rewrites the request internally to /index.html and serves it with a 200 OK status. The browser’s URL bar remains /about. This is crucial for SPAs, as it ensures your JavaScript application receives the request and can handle the routing logic. The ?userId=:id part in the last rule demonstrates how you can pass query parameters, which is also handled by the SPA’s routing.
The core problem Netlify redirects and rewrites solve is the disconnect between static file serving and dynamic application behavior, especially for SPAs. For traditional static sites, they offer SEO benefits (301 redirects for moved content) and cleaner URLs. For SPAs, they are essential for ensuring that all client-side routes are served by the main index.html file, allowing the JavaScript framework (React, Vue, Angular, etc.) to take over and render the correct view.
Netlify processes these rules in a specific order. Rules are evaluated from top to bottom in the netlify.toml file or the _redirects file. The first rule that matches the incoming request path is applied. If a rule has a status of 200 or 201, it’s considered a rewrite, meaning Netlify serves a different file than requested but keeps the original URL in the browser. If the status is 301 (Moved Permanently) or 302 (Found/Moved Temporarily), it’s a redirect, and the browser’s URL will change.
The power of Netlify’s redirect engine comes from its support for regular expressions and splat parameters. For example, you can create a rule like this in netlify.toml:
[[redirects]]
from = "/products/*"
to = "/products.html"
status = 200
This rule rewrites any request starting with /products/ to serve the products.html file, effectively treating all sub-paths under /products as if they were requesting the same page, which is common for category pages in e-commerce. The * is a wildcard that matches zero or more characters.
A common misconception is that you need to list every single possible route for an SPA. This is incorrect. For most SPAs, a single catch-all rewrite to index.html is sufficient. For example:
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
This rule, placed at the end of your redirect rules, will catch any request that hasn’t been matched by a preceding rule and rewrite it to index.html with a 200 status. This is the standard configuration for deploying SPAs. The crucial part here is that it must be the last rule, allowing specific redirects (like /old-page to /new-page) to take precedence.
The way Netlify handles these rules is by pre-compiling them into an efficient lookup table during the build process. When a request comes in, Netlify quickly checks this table. If a match is found, it performs the rewrite or redirect. If no specific rule matches, it falls back to serving a static file from your build output. This pre-compilation is why redirects and rewrites are extremely fast and don’t add latency to your site.
You can also use Netlify redirects to proxy API requests to a backend service, which is a powerful way to keep your frontend and backend decoupled while maintaining a single deployment point.
[[redirects]]
from = "/api/*"
to = "https://your-backend-api.com/:splat"
status = 200
Here, any request starting with /api/ will be proxied to https://your-backend-api.com/, with the * capturing any path segments and passing them along via the :splat parameter. This is a 200 status rewrite, so the browser’s URL won’t change.
The next logical step is understanding how to leverage Netlify Functions for more complex server-side logic triggered by these redirects.