errorpage
Principles
Error pages should do more than provide visual guidance: they should accurately communicate facts at the HTTP level while guiding humans, search engines, API clients, and AI agents toward their next action.
The status code takes priority. CDNs, load balancers, SDKs, browsers, and crawlers primarily determine the meaning of a response from its status line. A “404” or status field in the body is only supplementary information.
Do not create a soft 404 by returning a regular page with 200 OK for a nonexistent URL. At the HTTP level, this declares that the resource exists, causing crawlers, monitoring systems, SDKs, and caches to operate on a false assumption.
http
# すべて不適切
HTTP/1.1 200 OK ← 本文に <h1>404 Not Found</h1> があっても無意味
HTTP/1.1 200 OK ← 本文が { "status": 404 } でも無意味
HTTP/1.1 302 Found ← 代替がないのにトップへ飛ばす
Do not redirect to the homepage or search results when no replacement exists. It may appear user-friendly, but it misleads users into believing that the requested resource has moved. If the replacement is truly equivalent, use 301 or 308; otherwise, provide navigation in the body of a 404 or 410 response.
Selecting a Status Code
- 404 Not Found — The URL is incorrect, has no route, or its existence cannot be confirmed. It indicates that no representation currently exists, without explicitly stating that the resource has been permanently removed. It can also be used when revealing the existence of a resource is undesirable.
- 410 Gone — The resource existed in the past and has been intentionally and permanently retired. Choose it only when it is certain that the resource will not be restored and no replacement exists.
- 301 Moved Permanently / 308 Permanent Redirect — Use only when a reliably equivalent destination exists.
- 503 Service Unavailable — Maintenance, overload, or a dependency failure. The service is temporarily unavailable.
- 401 Unauthorized — Authentication information is required or insufficient.
- 403 Forbidden — Access is unavailable even when the requester is authenticated.
Whether an unauthorized resource is hidden with 404 or explicitly identified with 403 or 401 should be governed by a consistent security policy. Do not vary this behavior between pages.
Implementation Order
- At the routing layer, capture requests other than those for known pages, APIs, and static files.
- Use 301 or 308 only for mappings from old URLs to equivalent new URLs.
- Use 410 only for public URLs whose retirement has been confirmed.
- Use 404 for all other unknown URLs.
- Select the response body—HTML, Markdown, or application/problem+json—according to Accept.
- Add Vary: Accept to prevent different representations from being confused in the cache.
- Collect 404 logs and examine referrers, frequently requested URLs, and bot traffic to guide link corrections and redirects.
HTML Representation
html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>ページが見つかりません | Example</title>
<meta name="robots" content="noindex">
</head>
<body>
<main>
<h1>ページが見つかりません</h1>
<p>URL が間違っているか、ページが移動・削除された可能性があります。</p>
<nav aria-label="次の操作">
<a href="/">ホームへ戻る</a>
<a href="/search">サイト内を検索</a>
<a href="/docs/">ドキュメント</a>
<a href="/sitemap.xml">サイトマップ</a>
</nav>
</main>
</body>
</html>
- Explicitly state that the resource was not found. Saying only that an error occurred does not explain the cause.
- Provide links to the homepage, site search, major categories, documentation, and contact information.
- If displaying the requested URL, do not expose query strings, tokens, or email addresses unchanged.
- Follow the brand style while ensuring that the error page is not confused with a regular content page.
- noindex is not mandatory. The primary objective is to return the correct 4xx status. Add noindex if you want to prevent the 404 template itself from appearing in search results.
- Do not use robots.txt to completely block crawling of groups of nonexistent URLs. Doing so makes it impossible to verify that they return 404 or 410. To remove unwanted pages, first provide correct 404 and 410 responses, remove them from the sitemap, and fix internal links.
API Representation
Use RFC 9457 Problem Details.
json
{
"type": "https://example.com/problems/resource-not-found",
"title": "Resource not found",
"status": 404,
"detail": "No resource matches this URL. See https://example.com/docs/.",
"instance": "urn:request:01JABC..."
}
- The status in the body is advisory. It must always match the actual HTTP status line.
- The detail should explain how clients can correct the problem rather than expose debugging information.
- Do not require clients to parse strings and use them for control flow. Decisions should be based on the HTTP status and a stable type URI.
- When possible, make the type URI point to human-readable documentation.
- Ensure that clients can safely ignore unknown extension fields.
Markdown Representation
This representation is intended for agents. Markdown is not a replacement for 404; it is one possible body representation of a 404. The status must always remain 404.
md
# Not found
This URL does not exist.
- [Documentation index](/docs/)
- [Sitemap](/sitemap.xml)
- [LLM guidance](/llms.txt)
Do not return only Markdown to browsers that expect HTML. Use Accept-based content negotiation. Developer sites, API sites, and product documentation should provide discovery entry points so that failed requests do not become dead ends.
HEAD
HEAD /does-not-exist returns the same status as GET but does not return a body. A HEAD response contains no body.
Caching
404 and 410 responses may be cached. This is useful on sites that receive large volumes of requests for nonexistent URLs, but retaining resources that may return shortly after publication or within a short period can cause incidents.
- URLs that are completely retired and will never return — Use 410 with a short-to-medium cache lifetime.
- Typos, random URLs, or dynamically generated URL spaces — Use 404 with no-store or a short max-age.
- URLs that intentionally return 404 for authorization purposes — Use private, no-store to prevent information leakage through shared caches.
- When a CDN is present, verify the 404 behavior of the browser, CDN, and application layers separately.
Do Not Leak Information
Never include the following in an error body.
- Stack traces, exception messages, or source map URLs.
- Framework names and versions, internal IP addresses, or hostnames.
- Repository paths, database table names, or cloud bucket names.
- User IDs, email addresses, or session information.
- Response differences that reveal that a nonpublic resource exists but the requester lacks permission.
Problem Details is not a mechanism for exposing implementation debugging information.
Validation
sh
# ステータスだけ見る
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/no-such-path
# リダイレクトを追わず最初の応答を見る
curl -sS -D - -o /dev/null https://example.com/no-such-path
# HEAD でも同じか
curl -sSI https://example.com/no-such-path
# API 表現
curl -sS -i -H 'Accept: application/problem+json' https://example.com/api/no-such-path
# Markdown 表現
curl -sS -i -H 'Accept: text/markdown' https://example.com/no-such-path
Verify the following.
- The status line is genuinely 404 or the intended 410.
- There are no unintended 30x responses.
- The Content-Type matches the actual body. For JSON, it should be application/problem+json.
- If the body includes a status field, it matches the HTTP status.
- The HTML provides users with a way to recover, and the Markdown links are valid.
- The body contains no secrets or internal information.
- HEAD returns the same failure status without a body.
- The result is identical when accessed through the production domain via a CDN or proxy.