Error responses
When Cloudflare cannot complete a request, it generates an error response. The format depends on what the client requests via the Accept header and on the zone's Custom Errors configuration.
By default, error responses are HTML. Clients that request a structured format (such as application/json, application/problem+json, or text/markdown) receive a machine-readable response instead. This machine-readable response covers all 1xxx error codes (which return HTTP 4xx or 5xx status codes depending on the error) and Cloudflare-generated 5xx errors (500, 502, 504, 520-526). Responses for 5xx errors generated by the origin server are passed through by Cloudflare to the client and are not affected.
Cloudflare selects the response format based on the client's Accept header, following standard HTTP content negotiation ↗. When multiple formats are acceptable, quality factors (q values) determine precedence. At the same quality value, the first-listed type wins.
Accept header sent | Response format |
|---|---|
application/json | JSON (application/json; charset=utf-8) |
application/problem+json | JSON (application/problem+json; charset=utf-8) |
application/json, text/markdown;q=0.9 | JSON (higher quality factor) |
text/markdown | Markdown (text/markdown; charset=utf-8) |
text/markdown, application/json | Markdown (equal quality, first-listed wins) |
text/* | Markdown |
text/html | HTML |
*/* | HTML |
| Not set | HTML |
Structured error responses are available on all plans, including the Free plan. Custom Error Rules for overriding these responses require a Cloudflare paid plan.
Structured error responses are the default for zones with no custom error configuration. Zones that use Custom Errors retain full control over what clients receive.
What a client receives depends on which custom error features your zone has configured. Refer to the following sections for details.
This is the default for most zones. Cloudflare serves its default error response in the format the client requests.
| Client sends | Response |
|---|---|
Accept: application/json | Default Cloudflare structured JSON response |
Accept: text/markdown | Default Cloudflare structured Markdown response |
Accept: text/html | Default Cloudflare HTML error page |
No Accept header | Default Cloudflare HTML error page |
The zone has an Error Page uploaded via the Cloudflare dashboard. No Custom Error Rules are configured. The Error Page is served to all clients regardless of Accept header — Error Pages do not perform content negotiation.
| Client sends | Response |
|---|---|
Accept: application/json | Your custom HTML error page |
Accept: text/markdown | Your custom HTML error page |
Accept: text/html | Your custom HTML error page |
No Accept header | Your custom HTML error page |
If you want agents to receive structured responses while keeping your custom HTML for browsers, add a Custom Error Rule that matches on the Accept header. Refer to the next section for details.
The zone has one or more Custom Error Rules (available on paid plans). These take priority over Error Pages. You control what gets served, to whom, and under what conditions.
| Client sends | Response |
|---|---|
Accept: application/json | If a Custom Error Rule matches, the rule's content is served. If no rule matches, falls back to the Error Page (if configured) or the structured JSON response. |
Accept: text/markdown | If a Custom Error Rule matches, the rule's content is served. If no rule matches, falls back to the Error Page (if configured) or the structured Markdown response. |
Accept: text/html | If a Custom Error Rule matches, the rule's content is served. If no rule matches, falls back to the Error Page or the default HTML. |
No Accept header | Same fallback chain |
Custom Error Rules can match on any request header including Accept, and can target specific error codes. You can serve JSON to API clients, Markdown to agents, and HTML to browsers, all from the same zone.
Example: Serve custom JSON to API clients on a 522 error
This Custom Error Rule matches 522 errors where the client requests JSON:
Expression: (http.response.code eq 522) and (any(http.request.headers["accept"][*] contains "application/json"))
Action: Serve a custom JSON response with your own error format.
This rule takes priority over both the default structured JSON response and any configured Error Page. Clients that do not match the rule (for example, browsers requesting HTML) fall through to the Error Page or the default Cloudflare response.
Example: Serve structured responses to agents and a custom HTML page to browsers
If your zone has an Error Page configured, it is served to all clients, including agents requesting JSON or Markdown. To let agents receive Cloudflare's default structured responses instead, remove the Error Page. Without an Error Page, Cloudflare respects the Accept header automatically: agents get structured JSON or Markdown and browsers get HTML.
If you need to keep the Error Page for browsers but want to serve custom structured content to agents, create Custom Error Rules that match on the Accept header and serve your own JSON or Markdown content. Browsers that do not match either rule continue to receive your custom HTML Error Page.
When Cloudflare generates an error response, the following priority order determines what the client receives:
- Custom Error Rules — If a rule matches the error and request conditions, the rule's content is served.
- Error Pages — If an Error Page is configured for the error type and no Custom Error Rule matched, the Error Page is served as HTML regardless of the
Acceptheader. - Structured error responses — If no Custom Error Rule matched and no Error Page is configured, Cloudflare serves its default response in the format the client requested (JSON, Markdown, or HTML).
For the full priority order including account-level versus zone-level rules, WAF custom block responses, and security challenge pages, refer to the Custom Errors documentation.
{ "type": "https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-522/", "title": "Error 522: Connection timed out", "status": 522, "detail": "Cloudflare could not establish a TCP connection to the origin server. The TCP handshake timed out, which may indicate the origin is overloaded, firewalling Cloudflare, or unreachable at the network level.", "instance": "9f140b785e57c458", "error_code": 522, "error_name": "connection_timeout", "error_category": "origin", "ray_id": "9f140b785e57c458", "timestamp": "2026-04-24T09:22:40Z", "zone": "example.com", "cloudflare_error": true, "retryable": true, "retry_after": 120, "owner_action_required": true, "what_you_should_do": "**Wait and retry.** Back off for at least 120 seconds. If the error persists, the website operator should verify firewall rules and ensure the origin accepts connections from Cloudflare IP ranges.", "footer": "This error was generated by Cloudflare on behalf of the website owner."}---error_code: 522error_name: connection_timeouterror_category: originstatus: 522ray_id: 9f140b785e57c458timestamp: 2026-04-24T09:22:40Zzone: example.comcloudflare_error: trueretryable: trueretry_after: 120owner_action_required: true---
# Error 522: Connection timed out
## What Happened
Cloudflare could not establish a TCP connection to the origin server. The TCP handshake timed out, which may indicate the origin is overloaded, firewalling Cloudflare, or unreachable at the network level.
## What You Should Do
**Wait and retry.** Back off for at least 120 seconds. If the error persists, the website operator should verify firewall rules and ensure the origin accepts connections from Cloudflare IP ranges.
---
This error was generated by Cloudflare on behalf of the website owner.Fetch the structured JSON response for a 522 error:
curl --silent --compressed --header "Accept: application/json" \ --user-agent "TestAgent/1.0" --header "Accept-Encoding: gzip, deflate" \ "https://example.com/cdn-cgi/error/522" | jq .Fetch the structured Markdown response:
curl --silent --compressed --header "Accept: text/markdown" \ --user-agent "TestAgent/1.0" --header "Accept-Encoding: gzip, deflate" \ "https://example.com/cdn-cgi/error/522"Check for the Retry-After header on a retryable error:
curl --silent --compressed --dump-header - --output /dev/null \ --header "Accept: application/json" --user-agent "TestAgent/1.0" \ --header "Accept-Encoding: gzip, deflate" \ "https://example.com/cdn-cgi/error/521" | grep -i retry-afterBoth JSON and Markdown responses contain the same set of fields. JSON responses return them as a flat object; Markdown responses place them in YAML frontmatter followed by prose sections. The field definitions below apply to both formats.
JSON responses follow RFC 9457 (Problem Details for HTTP APIs) ↗. Any HTTP client that understands Problem Details can parse the five standard members (type, title, status, detail, instance) without Cloudflare-specific code.
| Field | Type | Description |
|---|---|---|
type | string | URI pointing to Cloudflare documentation for this error code. |
title | string | Short summary, for example, "Error 522: Connection timed out". |
status | integer | HTTP status code of the response. |
detail | string | Plain-text explanation of what went wrong and which party is responsible. |
instance | string | Ray ID identifying this specific error occurrence. |
| Field | Type | Description |
|---|---|---|
error_code | integer | Cloudflare error code (for example, 522, 1015). |
error_name | string | Machine-readable name in snake_case (for example, connection_timeout, rate_limited). Stable — suitable for programmatic matching. |
error_category | string | Fault classification. Refer to Error categories. Stable — suitable for programmatic matching. |
ray_id | string | Same value as instance. Included for compatibility with existing Cloudflare tooling. |
timestamp | string | ISO 8601 timestamp of when the error was generated. |
zone | string | The requested hostname. |
cloudflare_error | boolean | Always true. Confirms this error was generated by Cloudflare, not the origin. |
retryable | boolean | Whether the error is transient and the request can be retried. |
retry_after | integer or null | Seconds to wait before retrying. Present only when retryable is true. Matches the Retry-After HTTP header value. |
owner_action_required | boolean | Whether the site operator needs to take action to resolve the error. |
what_you_should_do | string | Actionable guidance for the client: what to do next, whether to retry, and who can fix the problem. |
footer | string | Attribution line. |
Markdown responses place these fields in YAML frontmatter (between --- delimiters), followed by three prose sections:
# Error {code}: {description}— heading with the error code and short description.## What Happened— corresponds to thedetailfield.## What You Should Do— corresponds to thewhat_you_should_dofield.
The frontmatter omits the RFC 9457 standard members (type, title, instance) and the footer field since these are either redundant with the prose or not applicable to the Markdown format.
The error_category field classifies the fault so that clients can route retry and escalation behavior without parsing the prose fields.
| Category | Codes | Meaning | Retry? |
|---|---|---|---|
origin | 502, 504, 520-524 | The origin server is responsible. Transient infrastructure failure. | Yes. Back off using retry_after. |
cloudflare | 500 | Cloudflare encountered an internal error. The origin was not necessarily involved. | Yes. Short retry (30s). |
ssl | 525, 526 | The origin's TLS configuration is broken (handshake failure or invalid certificate). | No. Retrying will not help until the operator fixes the TLS configuration. |
| Category | Meaning | Example codes |
|---|---|---|
access_denied | IP blocks, country blocks, firewall rules | 1005, 1006, 1007, 1008, 1010, 1012, 1106-1109 |
rate_limit | Rate limiting | 1015, 1025, 1027, 1200 |
dns | DNS resolution errors | 1001, 1016 |
config | Zone or origin configuration errors | 1004, 1014, 1033, 1043, 1047, 1049 |
tls | Client TLS errors (version, cipher, certificate) | 1017, 1028, 1029, 1044 |
legal | Legal restrictions (DMCA, country blocks) | 1026, 1039 |
worker | Worker script errors | 1042, 1100, 1101, 1102, 1103, 1104, 1105 |
rewrite | URL rewrite rule errors | 1036, 1037 |
snippet | Snippet configuration errors | 1201, 1202, 1203, 1204, 1205, 1206 |
unsupported | Unsupported features or protocols | 1045 |
Retryable error codes include a standard Retry-After ↗ HTTP response header. The header value in seconds matches the retry_after field in the response body.
| Code | retry_after (in seconds) |
|---|---|
| 500 | 30 |
| 502 | 60 |
| 504 | 120 |
| 520 | 60 |
| 521 | 120 |
| 522 | 120 |
| 523 | 120 |
| 524 | 120 |
| 525 | N/A (not retryable) |
| 526 | N/A (not retryable) |
Non-retryable codes (525, 526) do not include the Retry-After header.
Six retryable 1xxx error codes emit Retry-After:
| Code | retry_after (in seconds) | Error name |
|---|---|---|
| 1004 | 120 | DNS resolution error |
| 1015 | 30 | Rate limited |
| 1033 | 120 | Argo Tunnel error |
| 1038 | 60 | HTTP headers limit exceeded |
| 1200 | 60 | Cache connection limit |
| 1205 | 5 | Too many redirects |
All other 1xxx error codes are non-retryable and do not include the Retry-After header.
If a WAF rate limiting rule has already set a dynamic Retry-After value on the response, that value takes precedence over the default.