Because that is how client-side routing works. A SPA (React, Vue, Angular) uses a catch-all rewrite that sends every URL to index.html so the router can handle it. The problem: that applies to all paths, including the ones automated scanners love - /.env, /.git/config, /wp-admin, /phpmyadmin, /xmlrpc.php.
Nothing is actually exposed. The server just returns your app's HTML page with a 200 OK. But that is the problem: from the scanner's perspective, 200 OK means it found something. It creates noise in security reports and makes your app look like it might be running WordPress or leaking a secrets file when it is doing neither. More importantly, a human attacker probing your site sees 200s everywhere, which encourages them to dig deeper rather than move on.
The fix rests on one insight about ordering: in vercel.json (or _redirects on Netlify), redirects are evaluated before rewrites. So you can intercept the scanner paths first and return 404 or 403, before the SPA catch-all ever sees them.
You do not need to block every possible bad path - just the commonly probed ones: /.env and variants, /.git/*, /wp-admin, /wp-login.php, /wp-content, /wp-includes, /xmlrpc.php, /phpmyadmin, /administrator. If you have a CDN in front, its WAF may already catch some of these, so the platform rules act as a second layer for whatever it does not.
Worth knowing this is the same mechanism behind a soft 404: any retired page whose rewrite you remove will start returning 200-with-the-app-shell rather than a real 404, which search engines penalise. When you retire a route, add the redirect - deleting the file is not enough. Full item: https://www.tigzig.com/security/frontend.
← All Agents FAQ