diff --git a/docs/.vuepress/sets/craft-cloud.js b/docs/.vuepress/sets/craft-cloud.js
index ab193c5ea..7e02159d4 100644
--- a/docs/.vuepress/sets/craft-cloud.js
+++ b/docs/.vuepress/sets/craft-cloud.js
@@ -81,6 +81,7 @@ module.exports = {
title: "Help",
collapsable: false,
children: [
+ "diagnostics",
"faq",
"troubleshooting",
"security",
diff --git a/docs/.vuepress/theme/styles/code.pcss b/docs/.vuepress/theme/styles/code.pcss
index 673db3a25..68200ca46 100644
--- a/docs/.vuepress/theme/styles/code.pcss
+++ b/docs/.vuepress/theme/styles/code.pcss
@@ -91,6 +91,10 @@
}
}
+.cloud-diagnostics .theme-default-content code {
+ white-space: nowrap;
+}
+
/**
* Styling to emphasize placeholders within fenced code blocks.
* (This is something we added to VuePress.)
diff --git a/docs/cloud/diagnostics.md b/docs/cloud/diagnostics.md
new file mode 100644
index 000000000..5f5ff5083
--- /dev/null
+++ b/docs/cloud/diagnostics.md
@@ -0,0 +1,41 @@
+---
+description: Diagnose failed requests to Craft Cloud.
+pageClass: cloud-diagnostics
+---
+
+# Diagnostics
+
+## Response Headers
+
+- `cf-*`: Cloudflare
+- `x-gateway-*`: Craft Cloud gateway
+- All others: origin (Craft)
+
+| Response Header | Description |
+| --- | --- |
+| `x-gateway-flow` | How Craft Cloud produced the response. For example, `/origin/fetch` reached Craft. |
+| `x-gateway-http-signature` | [Request-signing](request-signing.md) result. `verified` succeeded; `unverified` was detected but failed validation. |
+| `cf-ray` | Cloudflare request ID.1 |
+| `cf-cache-status` | Cloudflare [cache status](static-caching.md#troubleshooting).1 |
+| `x-gateway-cf-ray` | Cloudflare request ID.2 |
+| `x-gateway-cf-cache-status` | Cloudflare [cache status](static-caching.md#troubleshooting).2 |
+
+1 With [Cloudflare O2O](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/) requests, the marked headers describe the customer zone.
+
+2 The marked headers describe the Craft Cloud zone and are only present with [Cloudflare O2O](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/) requests.
+
+## Interpreting Failures
+
+Status alone does not identify the response layer. Use it with the diagnostic
+headers:
+
+| Status | Response Headers | Interpretation |
+| --- | --- | --- |
+| `4xx/5xx` | `x-gateway-flow: /origin/fetch` | Returned by Craft; check application logs. |
+| `400/403/413` | `x-gateway-flow: /origin/reject` | Detected invalid or malicious request. |
+| `401` | `x-gateway-http-signature: unverified` | The signature was invalid or expired. |
+| `404` | `x-gateway-flow: /` | Hostname did not match a Craft Cloud environment. |
+| `429/503` | `Retry-After: 𝑛` | Automate retry, honoring `Retry-After`. |
+| `500/502` | No `x-gateway-flow: /origin/fetch` | A gateway error occurred; contact support if it persists. |
+| `504` | `x-gateway-flow: /origin/fetch/error` | The origin exceeded the [request duration limit](quotas.md#requests-responses). |
+| `5xx` | No `x-gateway-flow` | The error occurred before the gateway, either in the `craft.cloud` zone or a parent Cloudflare zone. |