To a normal user, 403 and 404 may look like similar failures. To an attacker, a consistent difference between them can reveal whether a protected resource exists. RFC 9110 explicitly allows a server to return 404 instead of 403 when it wants to conceal a forbidden resource. (RFC Editor)
What Are HTTP Status Codes?
When a server returns an HTTP response, its status code describes the result: 2xx success, 3xx redirection, 4xx client errors, and 5xx server errors. (MDN Web Docs)
They exist to tell a client what happened. They also tell everyone else, including someone asking questions on purpose.
How Do Attackers Use Them?
Enumeration by difference. A 404 normally means the resource was not found or the server is unwilling to disclose that it exists. A 403 means the server understood the request but refuses to fulfill it. A consistent 403/404 difference can therefore help with enumeration. (RFC Editor) Run a wordlist against an application and the 403 responses draw a map of protected directories, admin panels, and endpoints that were never meant to be discoverable.
Account enumeration. A login form that answers differently for a real username than a fake one hands over a user list. The same applies to password reset and registration.
Redirects. A 302 to a login page, where an authenticated user would receive 200, confirms both that the endpoint exists and that it is protected. Redirect behaviour often leaks what 403 and 404 handling was changed to hide.
Fingerprinting. Status codes combined with headers, error-page templates, redirects, and malformed-request behavior can help fingerprint the server or framework. Exact version identification usually requires additional evidence.
Verbose server errors. Verbose 5xx responses can expose stack traces, internal paths, database information, implementation details, and other sensitive debugging data. OWASP explicitly warns against exposing stack traces and internal error information. (OWASP Foundation)
Why Does It Matter?
None of this is exploitation. It is reconnaissance, and it decides where an attacker spends effort.
That makes it easy to dismiss. Low-volume enumeration can resemble ordinary application traffic, while high-volume probing may be detectable through repeated 401, 403, 404, or authentication failures.
Status Codes as a Malware Channel
Status codes are not only a leak. They have been a control channel.
Kaspersky discovered a new COMpfun variant in November 2019 and publicly detailed it in May 2020. The malware used uncommon HTTP status codes as C2 instructions. (Securelist)
COMpfun interpreted specific HTTP status codes as commands for actions including persistence, host fingerprinting, network-resource enumeration, USB propagation, uninstalling itself, and sending collected data. (Securelist)
The technique lets C2 instructions resemble HTTP responses, although unusual codes such as 422–429 can themselves become useful detection signals when seen unexpectedly.
Common Mistakes
- Returning distinguishable responses for sensitive existing and non-existing resources. This enables enumeration when attackers can reliably compare status, body, timing, or redirects.
- Normalising the code but nothing else. Response time and body length often still reveal what the status code was changed to hide.
- No controls against high-volume enumeration. Apply appropriate rate limits and detection to sensitive endpoints such as authentication, recovery, APIs, and resource discovery rather than blindly rate-limiting every error response.
How to Reduce the Risk
- Where revealing resource existence creates risk, normalize the externally visible response for unauthorized and nonexistent resources. RFC 9110 explicitly permits returning 404 to conceal a forbidden resource. (RFC Editor)
- Serve generic error pages, and keep the detail in server-side logs
- Normalize account-related responses so valid and invalid usernames cannot be distinguished through messages, status codes, redirects, body length, or meaningful timing differences. PortSwigger demonstrates enumeration through response wording, length, status codes, and timing. (PortSwigger)
- Remove server and framework version headers
- Set an explicit cache policy for error responses that may contain user-specific or sensitive information. Do not disable caching for every 4xx/5xx response as a blanket rule
- Use a WAF as a supporting control, not as the fix for verbose errors
What Should Security Testing Cover?
Ask your testers to establish:
- Whether 403 and 404 responses differ in a way that maps application structure
- Whether any 5xx response exposes stack traces, paths, or connection details
- Whether redirect behaviour distinguishes authenticated from unauthenticated requests
- Whether sustained enumeration generates an alert anyone acts on
How OraSec Can Help
Automated scanners can identify some verbose errors and response discrepancies, but manual testing is often needed to understand whether those differences create a useful enumeration path. OraSec's web application and API testing looks at the differences between responses, which is where the map is.
Conclusion
Status codes are not a vulnerability. They are a specification working correctly.
The risk is what the differences between them add up to. Two responses that should look identical, and do not, is enough to draw a map of an application from the outside.
FAQs
Should every error return the same code? No. Use semantically appropriate HTTP codes, but avoid unnecessary differences where those differences reveal sensitive resource or account existence.
Which responses are most dangerous? Verbose server-error responses can expose significant internal detail, but the risk comes primarily from the response body, headers, timing, and behavioral differences, not from the number 500 itself. (MDN Web Docs)



