As an app grows, its API tends to grow with it, often through a few rounds of "let's redo this endpoint properly" rather than one clean design from the start. What frequently doesn't happen at the same time is actually removing the earlier version once the new one ships, which means an older, less carefully built version of the same functionality is often still sitting there, still responding to requests.
Why this happens so naturally
Nobody makes an explicit decision to leave an old endpoint running forever. It's just that removing it feels riskier than leaving it alone, in case something you forgot about still depends on it, and there's rarely a forcing function that makes anyone go check. The new version ships, everyone moves on to the next feature, and the old one keeps existing by default, not by decision.
Why the old version is often the weaker one specifically
This is the part that makes it worth checking rather than just tolerating. If you've been improving your app's security posture over time, adding better auth checks, tightening access rules, adding rate limits, those improvements usually land on the current, active version of an endpoint. The retired-but-still-running old version often predates all of that work, meaning it can be the single least protected path into the same underlying data, sitting quietly alongside a much better-defended replacement.
How to actually find these
- Search your codebase for route definitions with version markers in the path, things like /v1/ alongside /v2/, and check whether the older one is actually still mounted and reachable, not just present in old code that's no longer wired up.
- Check your API documentation or route list for anything marked deprecated, and confirm deprecated actually means disabled, not just discouraged.
- Look at your server logs, if you have them, for any traffic still hitting an old path you assumed nobody used anymore.
- For each old endpoint still reachable, run the same checks you'd run on any endpoint: does it require auth, does it check the right user, is it rate-limited.
What to actually do about what you find
If nothing depends on the old version anymore, the cleanest fix is removing it entirely rather than trying to retroactively patch security onto something you were already planning to retire. If something still genuinely depends on it, at minimum bring it up to the same standard as the current version rather than letting it remain the weaker path by default.
This is one of the quieter gaps on this blog, precisely because nothing about it looks like an active mistake. It looks like old code nobody's using anymore. The problem is that old code nobody's using doesn't mean old code nobody can reach, and those are two very different states worth telling apart.