404 Not Found

Missing resources at the requested address are signaled by the 404 Not Found status code.

The response is cacheable by default. To override this behavior, the response must include appropriate HTTP caching headers.

Usage

When a 404 Not Found error arrives, the status does not specify whether the resource is permanently unavailable, temporarily unavailable, or never existed. This error most often results from mistyped URLs and is frequently seen by developers working with a set of resources still in progress.

Links to addresses returning 404 Not Found are commonly known as dead links or broken links. The status has also been used when a server is unwilling to acknowledge a resource exists.

When the server knows the resource once existed at the specified address but has been permanently removed, a 410 Gone status is more informative.

Because this error is so commonly seen by end-users, many servers use a custom error page descriptive and relevant for their site.

404 vs 410

Both codes report a resource the server will not return, and the difference is certainty.

404 Not Found says nothing about the future. A wrong address, a resource returning later, and an accidental deletion all produce the same response, and 404 distinguishes none of them. Servers answer 404 whenever the correct answer is unknown, which is most of the time.

410 is a deliberate statement. The resource existed, the removal was intentional, and no replacement is coming. Answering 410 requires knowing the history of the address, which is why applications rarely produce one automatically.

Search engines treat the pair identically: both codes remove an address from the index at the same rate, and crawling of the address tapers off once the removal registers, freeing crawl budget. Bing's webmaster guidelines name 404 as the correct signal for permanently removed content, keeping outdated URLs out of search results. Neither code transfers ranking signals, so a removed page holding traffic or links is better served by a 301 to a successor than by either code.

Answering 404 everywhere is safe. Reaching for 410 adds semantic precision for clients and tools, and value only where the removal is genuinely permanent and known.

Example

The client requests a resource and the server responds with 404 Not Found because the resource does not exist at the specified address.

Request

GET /documents/secret-formula.pdf HTTP/1.1
Host: www.example.re

Response

HTTP/1.1 404 Not Found
Content-Type: text/html
Content-Length: 178

<html>
  <head>
    <title>Resource Not Found</title>
  </head>
  <body>
    <p>The requested resource was not found.
    Check the spelling of the address.</p>
  </body>
</html>

IIS substatus codes

IIS records why a 404 happened in a substatus code, written to the sc-substatus field of the W3C log format. The status line on the wire stays a plain 404, and remote clients see no substatus by default. Detailed error pages render the code, and IIS keeps those pages local to the server unless an administrator raises the error mode.

A large share of the substatuses record a request-filtering denial rather than a missing file. A denied verb, a double-escaped URL, or an over-length query string all log a 404 even when the resource exists, so splitting log entries by substatus separates broken links from blocked requests.

Substatus Meaning
404.0 Not found, the file moved or does not exist
404.1 Site not found, the Host header matched no site binding
404.2 ISAPI or CGI restriction
404.3 MIME type restriction, the extension has no valid MIME mapping
404.4 No handler configured for the file extension
404.5 Request filtering: URL sequence denied
404.6 Request filtering: verb denied
404.7 Request filtering: file extension denied
404.8 Hidden namespace, the directory is hidden
404.9 File attribute hidden
404.10 Request header too long
404.11 Request filtering: double escape sequence
404.12 Request filtering: high-bit characters
404.13 Content length too large
404.14 Request filtering: URL too long
404.15 Request filtering: query string too long
404.16 WebDAV request sent to the static file handler
404.17 Dynamic content mapped to the static file handler
404.18 Request filtering: query string sequence denied
404.19 Denied by a request filtering rule
404.20 Too many URL segments
404.501 Dynamic IP restriction: concurrent request limit reached
404.502 Dynamic IP restriction: request rate limit reached
404.503 IP address on the deny list
404.504 Host name on the deny list

Two rows predate current guidance: request-filtering documentation now logs an oversized body as 413 substatus 413.1 and over-length headers as 431, superseding 404.13 and 404.10.

The 404.501 through 404.504 block mirrors identical entries under 401 and 403. The same four IP-restriction denials report under whichever of the three status codes the server configuration selects.

404.1 stands apart from the rest: the request reached the server, and no configured site matched the request. Fixing the site binding, or the DNS record pointing at the wrong server, resolves the error without touching any content.

How to fix

A 404 Not Found means no resource exists at the requested address.

  1. Verify the URL spelling and path. Typos in the path, filename, or query string are the most common cause. Double-check every segment of the address. Copy the path directly from the source link rather than typing manually.

  2. Check for case sensitivity. Linux-based servers treat /Page and /page as different paths. Match the exact casing of the original URL. Windows-based IIS servers are case-insensitive by default, so a 404 on IIS points to a genuinely missing resource.

  3. Look for a missing trailing slash. Some servers distinguish between /path and /path/. Add or remove the trailing slash and retry. nginx and Apache handle trailing slashes differently depending on try_files and DirectorySlash configuration.

  4. Confirm the resource exists on the server. Verify the file or route is deployed and accessible. SSH into the server and confirm the file exists at the expected document root path. In nginx, check the root or alias directive in the matching location block. In Apache, check DocumentRoot in the virtual host.

  5. Set up 301 or 308 Redirects for moved content. When a resource moves to a new address, redirect the old URL to prevent broken links. In nginx:

    location = /old-path {
        return 301 /new-path;
    }
    

    In Apache .htaccess:

    Redirect 301 /old-path /new-path
    
  6. Check server rewrite rules. Review mod_rewrite rules in Apache or try_files and location blocks in nginx. A misconfigured rewrite silently drops requests to valid paths. Enable rewrite logging to trace the rule evaluation. In Apache, set LogLevel alert rewrite:trace3 temporarily.

  7. Verify MIME types and handler mappings on IIS. IIS returns 404.3 when a file extension lacks a registered MIME type or handler mapping. Add the missing MIME type in IIS Manager or through web.config:

    <staticContent>
      <mimeMap fileExtension=".json"
        mimeType="application/json" />
    </staticContent>
    
  8. Check DNS and virtual host configuration. A domain pointing to the wrong server or a missing virtual host entry causes 404 for all paths. Verify the DNS A/CNAME records resolve to the correct server IP and the server has a matching server_name (nginx) or ServerName (Apache) directive.

  9. Audit broken links with a crawler. Run a site crawler to detect all 404 responses across the site. Fix broken internal links and submit an updated XML sitemap to search engines to accelerate re-indexing of moved content. Tools for finding broken links include Google Search Console (Coverage report), Screaming Frog, Ahrefs Site Audit, or wget --spider for command-line checking.

Code references

.NET

HttpStatusCode.NotFound

Rust

http::StatusCode::NOT_FOUND

Rails

:not_found

Go

http.StatusNotFound

Symfony

Response::HTTP_NOT_FOUND

Python3.5+

http.HTTPStatus.NOT_FOUND

Java

java.net.HttpURLConnection.HTTP_NOT_FOUND

Apache HttpComponents Core

org.apache.hc.core5.http.HttpStatus.SC_NOT_FOUND

Angular

@angular/common/http/HttpStatusCode.NotFound

See also

Last updated: August 18, 2026