Redirect users before they download your app
Client-side redirects — like React Router's <Redirect /> with a from prop, or similar mechanisms in other frameworks — are a common pattern. But they're the wrong tool for redirecting from a URL you already know about.
function App() {
return (
<BrowserRouter>
<Redirect from="/old-route" to="/new-route" />
{/* ... more routes etc... */}
</BrowserRouter>
)
}
That usage is distinct from the legitimately fine case of redirecting conditionally after the app loads, which is often necessary for authentication or authorization flows:
<Route exact path="/">
{loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>
The problem is the first pattern. If you know the URL you want users redirected from — say, you changed your URL scheme, or sent an email with a bad link — you should handle that redirect on the server.
Why client-side redirects cost you
The first issue is payload size. When a user hits /old-route, the browser doesn't know it's wrong until your entire app has downloaded and executed. Even with strong code splitting, a React app needs the core libraries before anything can run:
[email protected]— 6.3kB[email protected]— 114.6kB[email protected]— 28.4kB
That's roughly 149kB of JavaScript (minified, uncompressed) the user must download before the redirect can fire — and it doesn't include your own code. If the redirect points outside the app, that's a substantial waste. If it points within the app, code splitting can still trigger a waterfall of unnecessary requests.
The second problem is semantics. A client-side redirect can't set an HTTP status code. That means the browser cache and search engines never learn that the old URL is permanently replaced by the new one. The original URL keeps being treated as valid, and users keep paying the redirect tax.
Server-side alternatives
Server-side redirects solve both problems. They send the right response before any page code is delivered. Here's how three different server setups can handle the same redirect.
The case used here is a request to / that should go to /list, implemented with a 302 status code — a temporary redirect that leaves room for future changes without cache-busting concerns.
Netlify (production)
On Netlify, redirects can be declared in a _redirects file in your publish directory:
/ /list 302!
The same redirect can be configured in a netlify.toml file instead:
[[redirects]]
from = "/"
to = "/list"
status = 302
force = true
Local production preview
When running a built version of the app locally with serve, the underlying serve-handler package reads a serve.json configuration:
{
"redirects": [
{
"source": "/",
"destination": "/list",
"type": 302
}
]
}
Development server
Create React App's dev server can be customized through src/setupProxy.js, which exports a function receiving the Express app instance. An res.redirect call handles the root path:
function proxy(app) {
app.get(/^\/$/, (req, res) => res.redirect('/list'))
}
module.exports = proxy
The right tool for the job
The instinct to avoid a "simple" client-side redirect with boilerplate-free syntax is understandable:
<Redirect from="/" to="/list" />
But the server-side setup cost is one-time, and the benefits accumulate. When the web's native semantics handle your use case, use them — just as semantic HTML helps accessibility, server-side redirects are the correct way to tell browsers and search engines that a URL has moved. Every major hosting platform provides a mechanism for this. The effort is small, and correctness is worth it.



