From 8c751b25762936a5f8d221a0b1528891b2c9f7e7 Mon Sep 17 00:00:00 2001 From: Danish Mujtaba Qureshi Date: Thu, 5 Feb 2026 19:49:57 +0000 Subject: [PATCH 1/4] Add Mutual TLS (mTLS) authentication section --- .../privacy-proxy/concepts/authentication.mdx | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/content/docs/privacy-proxy/concepts/authentication.mdx b/src/content/docs/privacy-proxy/concepts/authentication.mdx index 1a15b12e024..b5e93dcf380 100644 --- a/src/content/docs/privacy-proxy/concepts/authentication.mdx +++ b/src/content/docs/privacy-proxy/concepts/authentication.mdx @@ -9,12 +9,13 @@ Privacy Proxy requires clients to authenticate before proxying traffic. This pag ## Authentication methods -Privacy Proxy supports two authentication methods: +Privacy Proxy supports three authentication methods: | Method | Use case | Privacy level | | -------- | ---------- | --------------- | | Pre-shared key (PSK) | Proof of concept, testing | Lower | -| Privacy Pass tokens | Production deployments | Higher | +| Privacy Pass tokens | Client to server | Higher | +| mTLS | Server to server | Higher | --- @@ -134,6 +135,24 @@ For production deployments using Privacy Pass: --- +## Mutual TLS (mTLS) + +[Mutual TLS (mTLS) authentication](https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/) ensures that traffic is both secure and trusted in both directions. The client presents a certificate to the proxy, and the proxy validates it before allowing the connection. + +### How it works + +The client includes a TLS client certificate during the TLS handshake. The proxy validates the certificate against a configured certificate authority (CA) and allows the connection if the certificate is trusted. + +### Limitations + +- **Certificate management**: You must provision and manage certificates for each client or service. +- **Not suitable for end users**: mTLS is designed for server-to-server communication, not for authenticating individual users. +- **No unlinkability**: The proxy can identify the client by its certificate, which reduces privacy compared to Privacy Pass. + +Use mTLS for server-to-server integrations where both parties are trusted services. + +--- + ## Authentication in double-hop deployments In [double-hop deployments](/privacy-proxy/concepts/deployment-models/#double-hop), authentication occurs at two levels: From d5f844de5e09a4682edb45efd656df7fab020b71 Mon Sep 17 00:00:00 2001 From: Danish Mujtaba Qureshi Date: Thu, 5 Feb 2026 20:00:04 +0000 Subject: [PATCH 2/4] Change Privacy Pass tokens privacy level to High --- src/content/docs/privacy-proxy/concepts/authentication.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/privacy-proxy/concepts/authentication.mdx b/src/content/docs/privacy-proxy/concepts/authentication.mdx index b5e93dcf380..cea2f6f5820 100644 --- a/src/content/docs/privacy-proxy/concepts/authentication.mdx +++ b/src/content/docs/privacy-proxy/concepts/authentication.mdx @@ -14,7 +14,7 @@ Privacy Proxy supports three authentication methods: | Method | Use case | Privacy level | | -------- | ---------- | --------------- | | Pre-shared key (PSK) | Proof of concept, testing | Lower | -| Privacy Pass tokens | Client to server | Higher | +| Privacy Pass tokens | Client to server | High | | mTLS | Server to server | Higher | --- From 6ed25ac2ff933239cb5bcc315a0e10de1793dcd1 Mon Sep 17 00:00:00 2001 From: danish-m-qureshi Date: Mon, 16 Mar 2026 17:15:59 +0000 Subject: [PATCH 3/4] Update Privacy Gateway metrics docs and remove alternating table row shading --- .../privacy-gateway/reference/metrics.mdx | 683 +++++++++++++++--- src/styles/table.css | 7 +- 2 files changed, 587 insertions(+), 103 deletions(-) diff --git a/src/content/docs/privacy-gateway/reference/metrics.mdx b/src/content/docs/privacy-gateway/reference/metrics.mdx index 8c4e4199cdf..469d11b27af 100644 --- a/src/content/docs/privacy-gateway/reference/metrics.mdx +++ b/src/content/docs/privacy-gateway/reference/metrics.mdx @@ -3,117 +3,606 @@ title: Privacy Gateway Metrics pcx_content_type: reference sidebar: order: 1 - --- -Privacy Gateway now supports enhanced monitoring through our GraphQL API, providing detailed insights into your gateway traffic and performance. To access these metrics, ensure you have: - -* A relay gateway proxy implementation where Cloudflare acts as the oblivious relay party. -* An API token with Analytics Read permissions. - We offer two GraphQL nodes to retrieve metrics: `ohttpMetricsAdaptive` and `ohttpMetricsAdaptiveGroups`. The first node provides comprehensive request data, while the second facilitates grouped analytics. - -## ohttpMetricsAdaptive - -The `ohttpMetricsAdaptive` node is designed for detailed insights into individual OHTTP requests with adaptive sampling. This node can help in understanding the performance and load on your server and client setup. - -### Key Arguments - - - -* `filter` required - * Apply filters to narrow down your data set. `accountTag` is a required filter. -* `limit` optional - * Specify the maximum number of records to return. -* `orderBy` optional - * Choose how to sort your data, with options for various dimensions and metrics. - - -### Available Fields - - - -* `bytesToClient` int optional - * The number of bytes returned to the client. -* `bytesToGateway` int optional - * Total bytes received from the client. -* `colo` string optional - * Airport code of the Cloudflare data center that served the request. -* `datetime` Time optional - * The date and time when the event was recorded. -* `gatewayStatusCode` int optional - * Status code returned by the gateway. -* `relayStatusCode` int optional - * Status code returned by the relay. - +Privacy Gateway exposes metrics through Cloudflare's [GraphQL Analytics API](/analytics/graphql-api/). All metrics are queryable via a single endpoint: -This node is useful for a granular view of traffic, helping you identify patterns, performance issues, or anomalies in your data flow. +``` +POST https://api.cloudflare.com/client/v4/graphql +``` -## ohttpMetricsAdaptiveGroups +To access your metrics, you need a Cloudflare API token with **Analytics Read** permissions. Refer to [Get started with the Analytics API](/analytics/graphql-api/getting-started/) for authentication details and example requests. -The `ohttpMetricsAdaptiveGroups` node allows for aggregated analysis of OHTTP request metrics with adaptive sampling. This node is particularly useful for identifying trends and patterns across different dimensions of your traffic and operations. - -### Key Arguments - - - -* `filter` required - * Apply filters to narrow down your data set. `accountTag` is a required filter. -* `limit` optional - * Specify the maximum number of records to return. -* `orderBy` optional - * Choose how to sort your data, with options for various dimensions and metrics. - - -### Available Fields - - - -* `count` int optional - * The number of records that meet the criteria. -* `dimensions` optional - * Specifies the grouping dimensions for your data. -* `sum` optional - * Aggregated totals for various metrics, per dimension. +--- +## Available nodes -**Dimensions** +Three GraphQL nodes are available, each corresponding to a different event type: -You can group your metrics by various dimensions to get a more segmented view of your data: +| Node | Description | +| ---- | ----------- | +| `ohttpRelayRequestMetricsAdaptiveGroups` | Per-request metrics — traffic volume and error analysis | +| `ohttpRelayIngressConnMetricsAdaptiveGroups` | Per client-to-relay connection — latency and protocol distribution | +| `ohttpRelayEgressConnMetricsAdaptiveGroups` | Per relay-to-gateway connection — relay-to-gateway latency | +:::note -* `colo` string optional - * The airport code of the Cloudflare data center. -* `date` Date optional - * The date of OHTTP request metrics. -* `datetimeFifteenMinutes` Time optional - * Timestamp truncated to fifteen minutes. -* `datetimeFiveMinutes` Time optional - * Timestamp truncated to five minutes. -* `datetimeHour` Time optional - * Timestamp truncated to the hour. -* `datetimeMinute` Time optional - * Timestamp truncated to the minute. -* `endpoint` string optional - * The appId that generated traffic. -* `gatewayStatusCode` int optional - * Status code returned by the gateway. -* `relayStatusCode` int optional - * Status code returned by the relay. +Each node has an independent adaptive bit-rate (ABR) sampling rate. Figures across nodes are not directly comparable. +::: -**Sum Fields** +--- -Sum fields offer a cumulative view of various metrics over your selected time period: +## ohttpRelayRequestMetricsAdaptiveGroups + +Use this node to monitor traffic volume and investigate errors. + +### Arguments + +| Argument | Required | Description | +| -------- | -------- | ----------- | +| `filter` | Yes | Filter your data. `accountTag` is always required. | +| `limit` | Yes | Maximum number of records to return. | +| `orderBy` | No | Sort order for results. | + +### Sum fields + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `requests` | uint64 | Total request count | +| `bytesToGateway` | uint64 | Total bytes sent from client to gateway | +| `bytesToClient` | uint64 | Total bytes sent from gateway back to client | + +### Dimensions + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `accountTag` | string | Your Cloudflare account identifier | +| `date` | Date | Calendar date (day granularity) | +| `datetimeMinute` | Time | Timestamp truncated to the minute | +| `datetimeFiveMinutes` | Time | Timestamp truncated to 5-minute intervals | +| `datetimeFifteenMinutes` | Time | Timestamp truncated to 15-minute intervals | +| `datetimeHour` | Time | Timestamp truncated to the hour | +| `colo` | string | Cloudflare data center that handled the request | +| `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | +| `relayStatusCode` | uint16 | HTTP status code returned by the relay to the client | +| `gatewayStatusCode` | uint16 | HTTP status code returned by the gateway to the relay | + +### Sample queries + +**Total requests by day** + +Get a high-level view of daily request volume and bytes transferred over a time range. + +```graphql +query DailyRequestVolume( + $accountTag: string! + $startDate: Date! + $endDate: Date! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayRequestMetricsAdaptiveGroups( + filter: { + date_geq: $startDate + date_leq: $endDate + endpoint: $endpoint + } + limit: 10000 + orderBy: [date_ASC] + ) { + sum { + requests + bytesToGateway + bytesToClient + } + dimensions { + date + endpoint + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "startDate": "2025-01-01", + "endDate": "2025-01-31", + "endpoint": "https://" +} +``` + +**Client request errors** + +Find client-side failures — such as malformed requests or auth failures — that never reached the gateway. These appear as relay `4xx` with no corresponding gateway status. + +```graphql +query ClientRequestErrors( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayRequestMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + relayStatusCode_geq: 400 + relayStatusCode_leq: 499 + gatewayStatusCode: 0 + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + sum { + requests + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + relayStatusCode + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +**Gateway response errors** + +Identify errors originating from the upstream gateway to determine if it is unhealthy. + +```graphql +query GatewayResponseErrors( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayRequestMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + gatewayStatusCode_geq: 500 + gatewayStatusCode_leq: 599 + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + sum { + requests + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + gatewayStatusCode + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +**Gateway timeouts** + +Detect incidents where the gateway did not respond within the configured timeout. + +```graphql +query GatewayTimeouts( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayRequestMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + relayStatusCode: 504 + gatewayStatusCode: 0 + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + sum { + requests + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +**Relay internal errors** + +Isolate relay-internal failures that are not caused by the gateway. + +```graphql +query RelayInternalErrors( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayRequestMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + relayStatusCode_geq: 500 + relayStatusCode_leq: 599 + gatewayStatusCode: 0 + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + sum { + requests + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + relayStatusCode + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +### Status code reference + +**relayStatusCode** + +| Code | Description | +| ---- | ----------- | +| `200` | Success — request relayed and response returned to client | +| `400` | Bad Request — malformed OHTTP capsule or invalid headers | +| `401` | Unauthorized — missing or invalid credentials on the outer request | +| `403` | Forbidden — client is not permitted to use this endpoint | +| `405` | Method Not Allowed — only POST is accepted for OHTTP | +| `413` | Payload Too Large — encapsulated request exceeds the size limit | +| `429` | Too Many Requests — client has been rate limited by the relay | +| `500` | Internal Server Error — relay failed to process the request | +| `502` | Bad Gateway — relay received an invalid response from the gateway | +| `503` | Service Unavailable — relay is temporarily unable to handle requests | +| `504` | Gateway Timeout — gateway did not respond within the configured timeout | + +**gatewayStatusCode** + +| Code | Description | +| ---- | ----------- | +| `200` | Success — gateway processed the inner OHTTP request | +| `400` | Bad Request — gateway could not parse the decapsulated inner request | +| `401` | Unauthorized — missing or invalid credentials on the inner request | +| `403` | Forbidden — gateway rejected the request based on policy | +| `404` | Not Found — target resource not found at the origin | +| `429` | Too Many Requests — gateway rate limited the request | +| `500` | Internal Server Error — gateway encountered a processing failure | +| `502` | Bad Gateway — gateway received an invalid response from the origin | +| `503` | Service Unavailable — gateway or origin is temporarily unavailable | +| _(empty)_ | Request never reached the gateway (relay-side failure or timeout) | +--- -* `bytesToClient` int optional - * Total bytes sent from the gateway to the client. -* `bytesToGateway` int optional - * Total bytes from the client to the gateway. -* `clientRequestErrors` int optional - * Total number of client request errors. -* `gatewayResponseErrors` int optional - * Total number of gateway response errors. +## ohttpRelayIngressConnMetricsAdaptiveGroups + +Use this node to monitor client-to-relay connection latency and protocol distribution. + +### Arguments + +| Argument | Required | Description | +| -------- | -------- | ----------- | +| `filter` | Yes | Filter your data. `accountTag` is always required. | +| `limit` | Yes | Maximum number of records to return. | +| `orderBy` | No | Sort order for results. | + +### Quantile fields + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `clientRelayRttMsP50` | float64 | Median round trip time between client and relay (ms) | +| `clientRelayRttMsP95` | float64 | 95th percentile RTT between client and relay (ms) | +| `clientRelayRttMsP99` | float64 | 99th percentile RTT between client and relay (ms) | + +### Dimensions + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `accountTag` | string | Your Cloudflare account identifier | +| `date` | Date | Calendar date (day granularity) | +| `datetimeMinute` | Time | Timestamp truncated to the minute | +| `datetimeFiveMinutes` | Time | Timestamp truncated to 5-minute intervals | +| `datetimeFifteenMinutes` | Time | Timestamp truncated to 15-minute intervals | +| `datetimeHour` | Time | Timestamp truncated to the hour | +| `colo` | string | Cloudflare data center that handled the request | +| `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | +| `httpVersion` | string | HTTP protocol version used for the client-to-relay connection | +| `tlsVersion` | string | TLS version negotiated on the client-to-relay connection | + +### Sample queries + +**Client-to-relay latency by data center** + +Compare latency across Cloudflare data centers to identify geographic performance issues. + +```graphql +query ClientRelayLatencyByColo( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayIngressConnMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + } + limit: 10000 + ) { + quantiles { + clientRelayRttMsP50 + clientRelayRttMsP95 + clientRelayRttMsP99 + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +**Protocol distribution** + +Monitor HTTP and TLS version adoption across your connections over time. + +```graphql +query ProtocolDistribution( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayIngressConnMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + httpVersion + tlsVersion + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` +--- -Utilize the ohttpMetricsAdaptiveGroups node to gain comprehensive, aggregated insights into your traffic patterns, helping you optimize performance and user experience. +## ohttpRelayEgressConnMetricsAdaptiveGroups + +Use this node to monitor relay-to-gateway connection latency. + +### Arguments + +| Argument | Required | Description | +| -------- | -------- | ----------- | +| `filter` | Yes | Filter your data. `accountTag` is always required. | +| `limit` | Yes | Maximum number of records to return. | +| `orderBy` | No | Sort order for results. | + +### Quantile fields + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `relayGatewayRttMsP50` | float64 | Median round trip time between relay and gateway (ms) | +| `relayGatewayRttMsP95` | float64 | 95th percentile RTT between relay and gateway (ms) | +| `relayGatewayRttMsP99` | float64 | 99th percentile RTT between relay and gateway (ms) | + +### Dimensions + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `accountTag` | string | Your Cloudflare account identifier | +| `date` | Date | Calendar date (day granularity) | +| `datetimeMinute` | Time | Timestamp truncated to the minute | +| `datetimeFiveMinutes` | Time | Timestamp truncated to 5-minute intervals | +| `datetimeFifteenMinutes` | Time | Timestamp truncated to 15-minute intervals | +| `datetimeHour` | Time | Timestamp truncated to the hour | +| `colo` | string | Cloudflare data center that handled the request | +| `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | + +### Sample queries + +**Relay-to-gateway latency by data center** + +Compare relay-to-gateway latency across data centers to detect routing or peering issues. + +```graphql +query RelayGatewayLatencyByColo( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayEgressConnMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + } + limit: 10000 + ) { + quantiles { + relayGatewayRttMsP50 + relayGatewayRttMsP95 + relayGatewayRttMsP99 + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + colo + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` + +**Relay-to-gateway latency over time** + +Track relay-to-gateway latency trends to detect gradual degradation. + +```graphql +query RelayGatewayLatencyOverTime( + $accountTag: string! + $start: Time! + $end: Time! + $endpoint: string! +) { + viewer { + accounts(filter: { accountTag: $accountTag }) { + ohttpRelayEgressConnMetricsAdaptiveGroups( + filter: { + datetime_geq: $start + datetime_leq: $end + endpoint: $endpoint + } + limit: 10000 + orderBy: [datetimeMinute_ASC] + ) { + quantiles { + relayGatewayRttMsP50 + relayGatewayRttMsP95 + relayGatewayRttMsP99 + } + dimensions { + datetimeMinute + datetimeFiveMinutes + datetimeHour + } + } + } + } +} +``` + +```json +{ + "accountTag": "", + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-01T23:59:59Z", + "endpoint": "https://" +} +``` diff --git a/src/styles/table.css b/src/styles/table.css index 6639aa0481d..1efca08602b 100644 --- a/src/styles/table.css +++ b/src/styles/table.css @@ -18,9 +18,4 @@ table * { padding-inline-end: 1rem; } -/* - Alternating row shading -*/ -.sl-markdown-content tr:nth-child(2n):not(:where(.not-content *)) { - background-color: var(--sl-color-gray-7, #1f1f1f); -} + From 5c1b1dc8733f2a17f6c9bddd31aa00ac2b2c0b05 Mon Sep 17 00:00:00 2001 From: danish-m-qureshi Date: Mon, 16 Mar 2026 20:21:14 +0000 Subject: [PATCH 4/4] Restructure Privacy Gateway metrics docs and clean up related resources --- .../privacy-gateway/reference/metrics.mdx | 337 ++++++++++-------- 1 file changed, 191 insertions(+), 146 deletions(-) diff --git a/src/content/docs/privacy-gateway/reference/metrics.mdx b/src/content/docs/privacy-gateway/reference/metrics.mdx index 469d11b27af..699341a130e 100644 --- a/src/content/docs/privacy-gateway/reference/metrics.mdx +++ b/src/content/docs/privacy-gateway/reference/metrics.mdx @@ -5,39 +5,58 @@ sidebar: order: 1 --- +import { Details } from "~/components"; + Privacy Gateway exposes metrics through Cloudflare's [GraphQL Analytics API](/analytics/graphql-api/). All metrics are queryable via a single endpoint: ``` POST https://api.cloudflare.com/client/v4/graphql ``` -To access your metrics, you need a Cloudflare API token with **Analytics Read** permissions. Refer to [Get started with the Analytics API](/analytics/graphql-api/getting-started/) for authentication details and example requests. +To access your metrics, you need a Cloudflare API token with **Analytics Read** permissions. Refer to [Get started with the Analytics API](/analytics/graphql-api/getting-started/) for authentication details. --- -## Available nodes +

Making a request

+ +The following example shows a complete request with authentication headers. Replace `` with your token and update the query body as needed. + +```bash +curl https://api.cloudflare.com/client/v4/graphql \ + --header "Authorization: Bearer " \ + --header "Content-Type: application/json" \ + --data '{ + "query": "query DailyRequestVolume($accountTag: string!, $startDate: Date!, $endDate: Date!, $endpoint: string!) { viewer { accounts(filter: { accountTag: $accountTag }) { ohttpRelayRequestMetricsAdaptiveGroups(filter: { date_geq: $startDate, date_leq: $endDate, endpoint: $endpoint }, limit: 10000, orderBy: [date_ASC]) { sum { requests bytesToGateway bytesToClient } dimensions { date endpoint } } } } }", + "variables": { + "accountTag": "", + "startDate": "", + "endDate": "", + "endpoint": "https://" + } + }' +``` -Three GraphQL nodes are available, each corresponding to a different event type: +--- -| Node | Description | -| ---- | ----------- | -| `ohttpRelayRequestMetricsAdaptiveGroups` | Per-request metrics — traffic volume and error analysis | -| `ohttpRelayIngressConnMetricsAdaptiveGroups` | Per client-to-relay connection — latency and protocol distribution | -| `ohttpRelayEgressConnMetricsAdaptiveGroups` | Per relay-to-gateway connection — relay-to-gateway latency | +

Available nodes

-:::note +Three GraphQL nodes are available, each corresponding to a different event type. All three nodes are aggregated-only — no raw per-connection records are exposed. -Each node has an independent adaptive bit-rate (ABR) sampling rate. Figures across nodes are not directly comparable. +| Node | Source event | Use for | +| ---- | ------------ | ------- | +| `ohttpRelayRequestMetricsAdaptiveGroups` | Per OHTTP request | Traffic volume and error analysis | +| `ohttpRelayIngressConnMetricsAdaptiveGroups` | Per client-to-relay connection | Client latency and protocol distribution | +| `ohttpRelayEgressConnMetricsAdaptiveGroups` | Per relay-to-gateway connection | Relay-to-gateway latency | -::: +:::note[Adaptive sampling] ---- +Each node is backed by an independent adaptive bit-rate (ABR) sampling pipeline. Cloudflare automatically scales the sampling rate based on traffic volume to keep query performance consistent. All returned values are extrapolated totals, not raw counts. Because each node has its own sampling rate, figures across nodes are not directly comparable. -## ohttpRelayRequestMetricsAdaptiveGroups +::: -Use this node to monitor traffic volume and investigate errors. +

Arguments

-### Arguments +All three nodes share the same argument signature. | Argument | Required | Description | | -------- | -------- | ----------- | @@ -45,15 +64,9 @@ Use this node to monitor traffic volume and investigate errors. | `limit` | Yes | Maximum number of records to return. | | `orderBy` | No | Sort order for results. | -### Sum fields +

Shared dimensions

-| Field | Type | Description | -| ----- | ---- | ----------- | -| `requests` | uint64 | Total request count | -| `bytesToGateway` | uint64 | Total bytes sent from client to gateway | -| `bytesToClient` | uint64 | Total bytes sent from gateway back to client | - -### Dimensions +The following dimensions are available on all three nodes. | Field | Type | Description | | ----- | ---- | ----------- | @@ -65,15 +78,76 @@ Use this node to monitor traffic volume and investigate errors. | `datetimeHour` | Time | Timestamp truncated to the hour | | `colo` | string | Cloudflare data center that handled the request | | `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | + +--- + +

ohttpRelayRequestMetricsAdaptiveGroups

+ +**Sum fields** + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `requests` | uint64 | Total request count | +| `bytesToGateway` | uint64 | Total bytes sent from client to gateway | +| `bytesToClient` | uint64 | Total bytes sent from gateway back to client | + +**Additional dimensions** + +| Field | Type | Description | +| ----- | ---- | ----------- | | `relayStatusCode` | uint16 | HTTP status code returned by the relay to the client | -| `gatewayStatusCode` | uint16 | HTTP status code returned by the gateway to the relay | +| `gatewayStatusCode` | uint16 | HTTP status code returned by the gateway to the relay. A value of `0` means the request never reached the gateway (relay-side failure or timeout). | -### Sample queries +**Status code reference** -**Total requests by day** +*relayStatusCode* + +| Code | Description | +| ---- | ----------- | +| **2xx — Success** | | +| `200` | Request relayed and response returned to client | +| **4xx — Client errors** | | +| `400` | Bad Request — malformed OHTTP capsule or invalid headers | +| `401` | Unauthorized — missing or invalid credentials on the outer request | +| `403` | Forbidden — client is not permitted to use this endpoint | +| `405` | Method Not Allowed — only POST is accepted for OHTTP | +| `413` | Payload Too Large — encapsulated request exceeds the size limit | +| `429` | Too Many Requests — client has been rate limited by the relay | +| **5xx — Server errors** | | +| `500` | Internal Server Error — relay failed to process the request | +| `502` | Bad Gateway — relay received an invalid response from the gateway | +| `503` | Service Unavailable — relay is temporarily unable to handle requests | +| `504` | Gateway Timeout — gateway did not respond within the configured timeout | + +*gatewayStatusCode* + +A value of `0` means the request never reached the gateway — the relay failed or timed out before forwarding it. + +| Code | Description | +| ---- | ----------- | +| **0 — No gateway response** | | +| `0` | Request never reached the gateway (relay-side failure or timeout) | +| **2xx — Success** | | +| `200` | Gateway processed the inner OHTTP request | +| **4xx — Client errors** | | +| `400` | Bad Request — gateway could not parse the decapsulated inner request | +| `401` | Unauthorized — missing or invalid credentials on the inner request | +| `403` | Forbidden — gateway rejected the request based on policy | +| `404` | Not Found — target resource not found at the origin | +| `429` | Too Many Requests — gateway rate limited the request | +| **5xx — Server errors** | | +| `500` | Internal Server Error — gateway encountered a processing failure | +| `502` | Bad Gateway — gateway received an invalid response from the origin | +| `503` | Service Unavailable — gateway or origin is temporarily unavailable | + +**Sample queries** + +

Total requests by day

Get a high-level view of daily request volume and bytes transferred over a time range. +
+ ```graphql query DailyRequestVolume( $accountTag: string! @@ -110,15 +184,19 @@ query DailyRequestVolume( ```json { "accountTag": "", - "startDate": "2025-01-01", - "endDate": "2025-01-31", + "startDate": "", + "endDate": "", "endpoint": "https://" } ``` -**Client request errors** +
+ +

Client request errors

-Find client-side failures — such as malformed requests or auth failures — that never reached the gateway. These appear as relay `4xx` with no corresponding gateway status. +Find client-side failures — such as malformed requests or auth failures — that never reached the gateway. These appear as relay `4xx` with `gatewayStatusCode: 0`. + +
```graphql query ClientRequestErrors( @@ -160,16 +238,20 @@ query ClientRequestErrors( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -**Gateway response errors** +
+ +

Gateway response errors

Identify errors originating from the upstream gateway to determine if it is unhealthy. +
+ ```graphql query GatewayResponseErrors( $accountTag: string! @@ -209,15 +291,19 @@ query GatewayResponseErrors( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -**Gateway timeouts** +
+ +

Gateway timeouts

-Detect incidents where the gateway did not respond within the configured timeout. +Detect incidents where the gateway did not respond within the configured timeout. These appear as `relayStatusCode: 504` with `gatewayStatusCode: 0`. + +
```graphql query GatewayTimeouts( @@ -257,15 +343,19 @@ query GatewayTimeouts( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -**Relay internal errors** +
+ +

Relay internal errors

-Isolate relay-internal failures that are not caused by the gateway. +Isolate relay-internal failures that are not caused by the gateway. These appear as relay `5xx` with `gatewayStatusCode: 0`. + +
```graphql query RelayInternalErrors( @@ -307,88 +397,41 @@ query RelayInternalErrors( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -### Status code reference - -**relayStatusCode** - -| Code | Description | -| ---- | ----------- | -| `200` | Success — request relayed and response returned to client | -| `400` | Bad Request — malformed OHTTP capsule or invalid headers | -| `401` | Unauthorized — missing or invalid credentials on the outer request | -| `403` | Forbidden — client is not permitted to use this endpoint | -| `405` | Method Not Allowed — only POST is accepted for OHTTP | -| `413` | Payload Too Large — encapsulated request exceeds the size limit | -| `429` | Too Many Requests — client has been rate limited by the relay | -| `500` | Internal Server Error — relay failed to process the request | -| `502` | Bad Gateway — relay received an invalid response from the gateway | -| `503` | Service Unavailable — relay is temporarily unable to handle requests | -| `504` | Gateway Timeout — gateway did not respond within the configured timeout | - -**gatewayStatusCode** - -| Code | Description | -| ---- | ----------- | -| `200` | Success — gateway processed the inner OHTTP request | -| `400` | Bad Request — gateway could not parse the decapsulated inner request | -| `401` | Unauthorized — missing or invalid credentials on the inner request | -| `403` | Forbidden — gateway rejected the request based on policy | -| `404` | Not Found — target resource not found at the origin | -| `429` | Too Many Requests — gateway rate limited the request | -| `500` | Internal Server Error — gateway encountered a processing failure | -| `502` | Bad Gateway — gateway received an invalid response from the origin | -| `503` | Service Unavailable — gateway or origin is temporarily unavailable | -| _(empty)_ | Request never reached the gateway (relay-side failure or timeout) | +
--- -## ohttpRelayIngressConnMetricsAdaptiveGroups +

ohttpRelayIngressConnMetricsAdaptiveGroups

-Use this node to monitor client-to-relay connection latency and protocol distribution. +**Quantile fields** -### Arguments +| Field | Type | Unit | Description | +| ----- | ---- | ---- | ----------- | +| `clientRelayRttMsP50` | float64 | ms | Median round trip time between client and relay | +| `clientRelayRttMsP95` | float64 | ms | 95th percentile RTT between client and relay | +| `clientRelayRttMsP99` | float64 | ms | 99th percentile RTT between client and relay | -| Argument | Required | Description | -| -------- | -------- | ----------- | -| `filter` | Yes | Filter your data. `accountTag` is always required. | -| `limit` | Yes | Maximum number of records to return. | -| `orderBy` | No | Sort order for results. | - -### Quantile fields - -| Field | Type | Description | -| ----- | ---- | ----------- | -| `clientRelayRttMsP50` | float64 | Median round trip time between client and relay (ms) | -| `clientRelayRttMsP95` | float64 | 95th percentile RTT between client and relay (ms) | -| `clientRelayRttMsP99` | float64 | 99th percentile RTT between client and relay (ms) | - -### Dimensions +**Additional dimensions** | Field | Type | Description | | ----- | ---- | ----------- | -| `accountTag` | string | Your Cloudflare account identifier | -| `date` | Date | Calendar date (day granularity) | -| `datetimeMinute` | Time | Timestamp truncated to the minute | -| `datetimeFiveMinutes` | Time | Timestamp truncated to 5-minute intervals | -| `datetimeFifteenMinutes` | Time | Timestamp truncated to 15-minute intervals | -| `datetimeHour` | Time | Timestamp truncated to the hour | -| `colo` | string | Cloudflare data center that handled the request | -| `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | | `httpVersion` | string | HTTP protocol version used for the client-to-relay connection | | `tlsVersion` | string | TLS version negotiated on the client-to-relay connection | -### Sample queries +**Sample queries** -**Client-to-relay latency by data center** +

Client-to-relay latency by data center

Compare latency across Cloudflare data centers to identify geographic performance issues. +
+ ```graphql query ClientRelayLatencyByColo( $accountTag: string! @@ -426,15 +469,19 @@ query ClientRelayLatencyByColo( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -**Protocol distribution** +
-Monitor HTTP and TLS version adoption across your connections over time. +

Protocol distribution

+ +Monitor HTTP and TLS version adoption across your connections over time. Each row in the result represents a distinct combination of `httpVersion` and `tlsVersion` observed in the selected time window. + +
```graphql query ProtocolDistribution( @@ -454,6 +501,9 @@ query ProtocolDistribution( limit: 10000 orderBy: [datetimeMinute_ASC] ) { + quantiles { + clientRelayRttMsP50 + } dimensions { datetimeMinute datetimeFiveMinutes @@ -470,53 +520,34 @@ query ProtocolDistribution( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` ---- - -## ohttpRelayEgressConnMetricsAdaptiveGroups - -Use this node to monitor relay-to-gateway connection latency. - -### Arguments +
-| Argument | Required | Description | -| -------- | -------- | ----------- | -| `filter` | Yes | Filter your data. `accountTag` is always required. | -| `limit` | Yes | Maximum number of records to return. | -| `orderBy` | No | Sort order for results. | - -### Quantile fields +--- -| Field | Type | Description | -| ----- | ---- | ----------- | -| `relayGatewayRttMsP50` | float64 | Median round trip time between relay and gateway (ms) | -| `relayGatewayRttMsP95` | float64 | 95th percentile RTT between relay and gateway (ms) | -| `relayGatewayRttMsP99` | float64 | 99th percentile RTT between relay and gateway (ms) | +

ohttpRelayEgressConnMetricsAdaptiveGroups

-### Dimensions +**Quantile fields** -| Field | Type | Description | -| ----- | ---- | ----------- | -| `accountTag` | string | Your Cloudflare account identifier | -| `date` | Date | Calendar date (day granularity) | -| `datetimeMinute` | Time | Timestamp truncated to the minute | -| `datetimeFiveMinutes` | Time | Timestamp truncated to 5-minute intervals | -| `datetimeFifteenMinutes` | Time | Timestamp truncated to 15-minute intervals | -| `datetimeHour` | Time | Timestamp truncated to the hour | -| `colo` | string | Cloudflare data center that handled the request | -| `endpoint` | string | The URL of the Privacy Gateway endpoint provided to you by Cloudflare | +| Field | Type | Unit | Description | +| ----- | ---- | ---- | ----------- | +| `relayGatewayRttMsP50` | float64 | ms | Median round trip time between relay and gateway | +| `relayGatewayRttMsP95` | float64 | ms | 95th percentile RTT between relay and gateway | +| `relayGatewayRttMsP99` | float64 | ms | 99th percentile RTT between relay and gateway | -### Sample queries +**Sample queries** -**Relay-to-gateway latency by data center** +

Relay-to-gateway latency by data center

Compare relay-to-gateway latency across data centers to detect routing or peering issues. +
+ ```graphql query RelayGatewayLatencyByColo( $accountTag: string! @@ -554,16 +585,20 @@ query RelayGatewayLatencyByColo( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` -**Relay-to-gateway latency over time** +
+ +

Relay-to-gateway latency over time

Track relay-to-gateway latency trends to detect gradual degradation. +
+ ```graphql query RelayGatewayLatencyOverTime( $accountTag: string! @@ -601,8 +636,18 @@ query RelayGatewayLatencyOverTime( ```json { "accountTag": "", - "start": "2025-01-01T00:00:00Z", - "end": "2025-01-01T23:59:59Z", + "start": "", + "end": "", "endpoint": "https://" } ``` + +
+ +--- + + + +- [GraphQL Analytics API — getting started](/analytics/graphql-api/getting-started/) — authentication, pagination, and rate limits +- [GraphQL Analytics API — filtering](/analytics/graphql-api/features/filtering/) — full filter syntax reference +- [Create an API token](/fundamentals/api/get-started/create-token/) — set up a token with Analytics Read permissions