Cloudflare 5xx: Trace 504 Errors from Edge to Origin

Separate the edge and origin with the same request

When a site behind Cloudflare is slow or returns 520, 521, 522, 524, 525, or 526, changing DNS, WAF, cache, Tunnel, and the origin web server together destroys the first useful failure boundary. Compare the edge response received by the user with the response produced by the origin under the same URL, time, and Host conditions.

Record the full URL, timestamp and timezone, HTTP status, and Cf-Ray as one incident key. Cf-Ray connects a request across Cloudflare and origin logs; it does not identify the cause by itself. A cache value such as HIT, MISS, or DYNAMIC also does not prove success or failure on its own.

A real 504 investigation: notification time was not failure time

On 2026-10-22, we investigated this site’s Search Console server-error notification. The current path is Cloudflare Tunnel → Nginx → PHP-FPM → WordPress/MariaDB. Comparing origin logs, Google’s stored inspection result, and current requests separated different observations. Times below are UTC; add nine hours for Korea Standard Time.

# Observed origin records; IPs and other identifiers omitted
2026-10-16T17:17:40Z  GET /privacy-policy/  status=504
upstream timed out ... while reading response header from upstream
UTC time Available observation Limit of interpretation
Oct 16, 17:17:40 /privacy-policy/ origin access log: 504; error log: upstream response-header timeout Nginx failed while waiting for FastCGI response headers. The slow PHP function or database request is unknown
Oct 20, 01:36:41 Stored Google inspection of the same URL: successful fetch, indexed A successful later crawl; not a current live test
Oct 22, 07:56:26 Search Console server-error email received Searching only around the email time would miss the recorded failure
Oct 22 investigation All 130 unique current sitemap URLs returned HTTP 200 Establishes current reachability, not the absence of historical failures

Among 102,944 retained origin requests from September 12 through October 22, this was the only origin 5xx. However, the complete article inspection also found stored Google 5xx records for three other URLs; all three currently returned 200. We did not label those Cloudflare failures merely because no matching origin error was retained. Without the historical edge response and Ray ID, the failing layer for those three remains unknown.

The origin User-Agent said Googlebot, but the origin connection address behind the tunnel could not verify Google’s actual client IP. A request with a Googlebot User-Agent does not replace a real Google request. The URL Inspection API returns Google’s stored state, so a current curl 200 can coexist with a historical Google 5xx.

Established by this case: one historical FastCGI timeout, a later successful stored crawl, and current reachability. We cannot attribute it to a plugin, insufficient memory, or a PHP vulnerability, or claim today’s service restarts repaired that incident. A recurrence needs matching timestamps, Ray IDs, origin durations, and PHP slow logs to narrow the cause further.

A normal baseline captured from the Nole IT stack

The following baseline was captured on 2026-08-08 from this site’s Synology NAS, Docker WordPress, MariaDB, and Cloudflare Tunnel/DNS path. Identifying values were removed. This is not a deliberately reproduced outage. It preserves the layers that answer during normal operation so a later incident can be compared against the same boundary.

# 2026-08-08, identifying values redacted
EDGE    HTTP 200 | server=cloudflare | cf-cache-status=DYNAMIC | cf-ray=present
ORIGIN  HTTP 200 | server=Apache/2.4.x (Debian)
DNS     two proxied A answers
TLS     wildcard SAN matched | certificate valid at test time

Both the edge and origin returned HTTP 200 in this baseline, so the delivery path from Cloudflare to the origin was working at that time. cf-cache-status=DYNAMIC means this WordPress response was not a normal cache hit; it is not evidence of latency. During the next incident, the same commands must show whether only the origin is fast or both paths are slow.

A separate healthy observation after the Oracle move

At 11:21 UTC on September 13, 2026, read-only checks returned 200 for both the public HTTPS home and the current Oracle origin HTTP home with the same Host over the management connection. These were separate requests, not a load or latency comparison or reproduced 5xx recovery. The August 8 NAS results above remain historical observations.

Oracle migration verification record

Values to capture before a change

Layer Values to capture Decision rule
Client and edge URL, time, status, Cf-Ray, cf-error-type A response with Cf-Ray can be traced through Cloudflare
Cache cf-cache-status, response time, retry result Interpret cache state together with origin contact
Origin Direct response with the same Host, web log, application log Compare edge and origin status and timing
TLS SNI, certificate SAN, expiry, origin TLS log For 525 and 526, repair the certificate path before retrying
Security rules Security Events action, rule ID, request time Consider the narrowest WAF exception only when an event exists
curl -sS -D edge.headers -o /dev/null https://example.com/
curl -sS -D origin.headers -o /dev/null -H 'Host: example.com' http://<ORIGIN_IP>:<PORT>/
grep -Ei '^(HTTP/|server:|cf-ray:|cf-cache-status:|cf-error-type:|cf-error-origin:)' edge.headers

Run a direct-origin request only from a management network or an authenticated diagnostic path. Do not disable the public DNS proxy or expose the origin address for this test. A Tunnel deployment may not permit public origin-IP bypass, so send the same Host header from the origin LAN or Docker host instead.

Where each 5xx code points first

Status Scope identified by Cloudflare First place to inspect
520 An empty, unknown, or unexpected origin response PHP/web-server crash, oversized headers, early close
521 The origin web server refused the connection or is down Listen port, service state, blocked Cloudflare addresses
522 TCP connection timeout between Cloudflare and origin Firewall, routing, origin load, connection count
524 Timeout while waiting after the origin connection Slow request, database lock, upstream processing time
525 TLS handshake failed between Cloudflare and origin SNI, cipher, origin certificate, TLS error log
526 Origin certificate validation failed Certificate chain, hostname, expiry, SSL mode

Do not decide from the error page alone whether 500, 502, 503, or 504 was generated by Cloudflare or passed through from the origin. Compare cf-error-type, origin logs, and edge/origin status in Error Analytics. Current Cloudflare-generated error responses can provide structured data when requested with Accept: application/json, but that behavior does not rewrite an origin-generated 5xx body.

curl -sS --compressed -H 'Accept: application/json' https://example.com/cdn-cgi/error/522
curl -sS --compressed -D - -o /dev/null -H 'Accept: application/json' https://example.com/cdn-cgi/error/521

These paths demonstrate error-response formats. A 522 response from /cdn-cgi/error/522 does not prove that your origin timed out. Diagnose the original URL that actually failed for a user. Store demonstration responses, healthy baselines, and real incident logs as different types of evidence.

The HTTP origin comparison applies when the Tunnel connects to that HTTP service. If the origin itself uses HTTPS, do not downgrade it for comparison: keep the original hostname and SNI in an HTTPS request. A valid edge certificate does not establish that origin certificate validation also succeeded.

Align the logs in chronological order

  1. Reproduce one failing request and record its URL, UTC or explicitly named local time, status, and Cf-Ray. Do not mix several refresh attempts.
  2. Check Cloudflare Security Events for a blocking action at the same time. If there is no event, stop treating WAF disablement as the next step.
  3. Where the plan and log product allow it, search for Cf-Ray or Ray ID and compare edge status with origin status. Log access differs by plan.
  4. Find the same time and Cf-Ray in origin access and error logs. If the request never reached the origin log, inspect Tunnel, firewall, and TLS before editing the application.
  5. If the origin logged HTTP 200 while the edge failed, narrow oversized headers, early close, and Transform/Worker rules. If the origin also logged 5xx, prioritize the application and database logs.

A Logpush OriginResponseStatus value of 0 is not always an origin failure. When CacheStatus in the same record is hit or revalidated, Cloudflare may have served a normal cache path without contacting the origin. When CacheStatus is miss or expired and OriginResponseStatus is 0, there is evidence that the origin connection ended before valid HTTP response headers arrived.

Change one layer at a time

  • Only when Security Events contains the actual rule ID should a Skip rule be narrowed to the affected path, method, and source conditions. Avoid disabling the entire WAF even as a comparison test.
  • For 521 or 522, restore the origin listener and Cloudflare/Tunnel connection first. Cache purge cannot repair a failed TCP connection.
  • For 524, measure origin processing time, database locks, and external API waits before increasing a timeout. A larger value can hide the slow path.
  • For 525 or 526, do not leave the zone in Flexible mode. Repair the origin certificate, SNI, chain, and Cloudflare SSL mode so they agree.
  • When only one path fails, compare one Worker, Redirect Rule, or Transform Rule at a time and record its original value and priority.

Verification and rollback

After a change, repeat both the edge and direct-origin requests with the original URL and request conditions. Do not stop when the status changes to HTTP 200; also confirm response time, Cf-Ray traceability, and arrival in the origin log. Retain one normal request and one formerly failing request to reduce cache-only success.

Save every rule expression, action, and priority before editing. Keep origin configuration in a file backup or deployable revision. If the fault moves to another layer or valid traffic becomes blocked, roll back only the last change and compare it with the baseline again.

Official references