RoastMyOpsec

Vulnerabilities

Soft 404 vs Hard 404 for Sensitive Paths

December 1, 2025

Soft 404 vs hard 404 for sensitive paths is why status codes lie. A hard 404 (or 403/401) with a tiny generic body usually means the file is not there or is denied. A soft 404 is a 200 that returns your marketing HTML for every unknown path — including /.git/HEAD and /.env. Scanners that only check the status line will pass or fail the wrong test. RoastMyOpsec looks for file-specific signatures and soft-404 behavior on bounded path probes. Deny VCS and env paths at the CDN even when the SPA would have rendered a homepage.

The practical difference

A hard 404 is an honest miss: the origin or edge says not found. A 403 is an honest deny. A 200 with application/json that is actually a key-value env dump is an incident. A 200 with text/html that is your React shell is a routing default, not a treasure chest — unless the body also matches a real file signature.

Redirect-to-home on every unknown path is the same class of confusion as a soft 404.

How to choose what to configure

At the edge, deny or 404 well-known sensitive prefixes before they hit the SPA. Let the app 200 only for real document routes. Do not use 'always index.html' for /.git, /.env, /backup.zip, or *.map if those files must never exist in production.

ResponseLikely meaningOPSEC moveTakeaway
404 + small generic bodyNot present or blockedStill deny at edge foreverHonest miss
200 + SPA HTMLSoft 404 / client routerExclude sensitive prefixes from the rewriteStatus 200 is not a file
200 + env/git signatureReal leakRotate, deny, incident processSee .git vs .env guide
301/302 to /Catch-all redirectStop treating it as 404Same confusion as a soft 404

When a 'clean' scan still needs an edge rule

A marketing SPA can look clean because every probe returns the shell. That is not the same as a WAF deny. Add explicit denies so a future mis-deploy cannot publish .git next to index.html.

Preview deployments that skip those rules are how 'prod is fine' still leaks.

Common mistakes

The first mistake is trusting curl -I status codes alone.

The second mistake is a single rewrite-to-index for the entire hostname.

The third mistake is blocking in robots.txt and not at the CDN.

Free audit the URL you own

RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.

Free audit now

FAQ

Does a 200 on /.env always mean the file leaked?
No. On many SPAs it is the homepage HTML. Confirm Content-Type and whether the body looks like env keys versus your app shell.
Should I still block /.git if I get a soft 404?
Yes. Edge deny is cheap and survives the next accidental upload.
How does RoastMyOpsec tell them apart?
Bounded GETs plus content signatures and soft-404 checks — not status codes alone. No recursive git dump.

Sources

Related guides