Cloudflare Prometheus Exporter

Export Cloudflare metrics to Prometheus. Built on Cloudflare Workers with Durable Objects for stateful metric accumulation.

Features
- 90+ Prometheus metrics - requests, bandwidth, threats, workers, load balancers, SSL certs, hostname-level analytics, network analytics, Magic Transit tunnel health/traffic/SLO, Magic Firewall per-rule visibility, stream video/live, and more
- Cloudflare Workers - serverless edge deployment
- Durable Objects - stateful counter accumulation for proper Prometheus semantics
- Background refresh - alarms fetch data every 60s; scrapes return cached data instantly
- Rate limiting - 200 req/10s with exponential backoff
- Multi-account - automatically discovers and exports all accessible accounts/zones
- Runtime config API - change settings without redeployment via REST endpoints
- Configurable - zone filtering, metric denylist, label exclusion, custom metrics path, and more
Quick Start
One-Click Deploy
Click the deploy button above. Configure CLOUDFLARE_API_TOKEN as a secret after deployment. Configure BASIC_AUTH_USER and BASIC_AUTH_PASSWORD to protect the exporter with HTTP Basic Auth.
Manual Deployment
git clone https://github.com/cloudflare/cloudflare-prometheus-exporter.git
cd cloudflare-prometheus-exporter
bun install
wrangler secret put CLOUDFLARE_API_TOKEN
bun run deploy
Configuration
Configuration is resolved in order: KV overrides → env vars → defaults. Use the Runtime Config API for dynamic changes without redeployment.
Environment Variables
Set in wrangler.jsonc or via wrangler secret put:
| Variable |
Default |
Description |
CLOUDFLARE_API_TOKEN |
- |
Cloudflare API token (secret) |
QUERY_LIMIT |
10000 |
Max results per GraphQL query |
SCRAPE_DELAY_SECONDS |
300 |
Delay before fetching metrics (data propagation) |
TIME_WINDOW_SECONDS |
60 |
Query time window |
METRIC_REFRESH_INTERVAL_SECONDS |
60 |
Background refresh interval |
LOG_LEVEL |
info |
Log level (debug/info/warn/error) |
LOG_FORMAT |
json |
Log format (pretty/json) |
ACCOUNT_LIST_CACHE_TTL_SECONDS |
600 |
Account list cache TTL |
ZONE_LIST_CACHE_TTL_SECONDS |
1800 |
Zone list cache TTL |
SSL_CERTS_CACHE_TTL_SECONDS |
1800 |
SSL cert cache TTL |
HEALTH_CHECK_CACHE_TTL_SECONDS |
10 |
Health check cache TTL |
EXCLUDE_HOST |
false |
Exclude host labels from metrics |
CF_HTTP_STATUS_GROUP |
false |
Group HTTP status codes (2xx, 4xx, etc.) |
DISABLE_UI |
false |
Disable landing page (returns 404) |
DISABLE_CONFIG_API |
false |
Disable config API endpoints (returns 404) |
METRICS_DENYLIST |
- |
Comma-separated list of metrics to exclude |
CF_ACCOUNTS |
- |
Comma-separated account IDs to include (default: all) |
CF_ZONES |
- |
Comma-separated zone IDs to include (default: all) |
CF_FREE_TIER_ACCOUNTS |
- |
Comma-separated account IDs using free tier (skips paid-tier metrics) |
HOST_METRICS_ALLOWLIST |
- |
Comma-separated hostnames for hostname-level metrics (max 50). Empty disables. Adds 1 extra GraphQL call per account per refresh cycle. EXCLUDE_HOST=true also disables. |
HOST_METRICS_DELAY_SECONDS |
60 |
Ingestion delay for hostname metrics (seconds). Lower values = fresher data for alerting but risk incomplete data. Independent from SCRAPE_DELAY_SECONDS. |
METRICS_PATH |
/metrics |
Custom path for metrics endpoint |
BASIC_AUTH_USER |
- |
Username for basic auth (secret, default: no auth, requires BASIC_AUTH_PASSWORD) |
BASIC_AUTH_PASSWORD |
- |
Password for basic auth (secret, default: no auth, requires BASIC_AUTH_USER) |
Creating an API Token
Quick setup: Create token with pre-filled permissions
Manual setup:
| Permission |
Access |
Required |
| Zone > Analytics |
Read |
Yes |
| Account > Account Analytics |
Read |
Yes |
| Account > Workers Scripts |
Read |
Yes |
| Zone > SSL and Certificates |
Read |
Optional |
| Zone > Firewall Services |
Read |
Optional |
| Zone > Load Balancers |
Read |
Optional |
| Account > Logs |
Read |
Optional |
| Account > Magic Transit |
Read |
Optional |
Endpoints
| Path |
Method |
Description |
/ |
GET |
Landing page (disable: DISABLE_UI) |
/metrics |
GET |
Prometheus metrics |
/health |
GET |
Health check ({"status":"healthy"}) |
/config |
GET |
Get all runtime config (disable: DISABLE_CONFIG_API) |
/config |
DELETE |
Reset all config to env defaults (disable: DISABLE_CONFIG_API) |
/config/:key |
GET |
Get single config value (disable: DISABLE_CONFIG_API) |
/config/:key |
PUT |
Set config override (persisted in KV) (disable: DISABLE_CONFIG_API) |
/config/:key |
DELETE |
Reset config key to env default (disable: DISABLE_CONFIG_API) |
Prometheus Configuration
scrape_configs:
- job_name: 'cloudflare'
scrape_interval: 60s
scrape_timeout: 30s
static_configs:
- targets: ['your-worker.your-subdomain.workers.dev']
With Basic Auth
Set up basic auth to protect all endpoints:
wrangler secret put BASIC_AUTH_USER
wrangler secret put BASIC_AUTH_PASSWORD
Then configure Prometheus:
scrape_configs:
- job_name: 'cloudflare'
scrape_interval: 60s
scrape_timeout: 30s
basic_auth:
username: 'your-username'
password: 'your-password'
static_configs:
- targets: ['your-worker.your-subdomain.workers.dev']
Runtime Config API
Override configuration at runtime without redeployment. Overrides persist in KV and take precedence over wrangler.jsonc env vars.
Config Keys
| Key |
Type |
Description |
queryLimit |
number |
Max results per GraphQL query |
scrapeDelaySeconds |
number |
Delay before fetching metrics |
timeWindowSeconds |
number |
Query time window |
metricRefreshIntervalSeconds |
number |
Background refresh interval |
accountListCacheTtlSeconds |
number |
Account list cache TTL |
zoneListCacheTtlSeconds |
number |
Zone list cache TTL |
sslCertsCacheTtlSeconds |
number |
SSL cert cache TTL |
healthCheckCacheTtlSeconds |
number |
Health check cache TTL |
logFormat |
"json" | "pretty" |
Log format |
logLevel |
"debug" | "info" | "warn" | "error" |
Log level |
cfAccounts |
string | null |
Comma-separated account IDs (null = all) |
cfZones |
string | null |
Comma-separated zone IDs (null = all) |
cfFreeTierAccounts |
string |
Comma-separated free tier account IDs |
metricsDenylist |
string |
Comma-separated metrics to exclude |
excludeHost |
boolean |
Exclude host labels |
httpStatusGroup |
boolean |
Group HTTP status codes |
hostMetricsAllowlist |
string |
Comma-separated hostnames for hostname-level metrics |
hostMetricsDelaySeconds |
number |
Ingestion delay for hostname metrics (seconds) |
Examples
# Get all config
curl https://your-worker.workers.dev/config
# Get single value
curl https://your-worker.workers.dev/config/logLevel
# Set override
curl -X PUT https://your-worker.workers.dev/config/logLevel \
-H "Content-Type: application/json" \
-d '{"value": "debug"}'
# Filter to specific zones
curl -X PUT https://your-worker.workers.dev/config/cfZones \
-H "Content-Type: application/json" \
-d '{"value": "zone-id-1,zone-id-2"}'
# Reset to env default
curl -X DELETE https://your-worker.workers.dev/config/logLevel
# Reset all overrides
curl -X DELETE https://your-worker.workers.dev/config
Available Metrics
Zone Request Metrics
| Metric |
Type |
Labels |
cloudflare_zone_requests_total |
counter |
zone |
cloudflare_zone_requests_cached |
gauge |
zone |
cloudflare_zone_requests_ssl_encrypted_total |
counter |
zone |
cloudflare_zone_requests_content_type_total |
counter |
zone, content_type |
cloudflare_zone_requests_country_total |
counter |
zone, country |
cloudflare_zone_requests_status_total |
counter |
zone, status |
cloudflare_zone_requests_browser_map_page_views_total |
counter |
zone, family |
cloudflare_zone_requests_ip_class_total |
counter |
zone, ip_class |
cloudflare_zone_requests_ssl_protocol_total |
counter |
zone, ssl_protocol |
cloudflare_zone_requests_http_version_total |
counter |
zone, http_version |
cloudflare_zone_requests_origin_status_country_host_total |
counter |
zone, origin_status, country, host |
cloudflare_zone_requests_status_country_host_total |
counter |
zone, edge_status, country, host |
cloudflare_zone_requests_by_method_total |
counter |
zone, method |
Zone Bandwidth Metrics
| Metric |
Type |
Labels |
cloudflare_zone_bandwidth_total |
counter |
zone |
cloudflare_zone_bandwidth_cached_total |
counter |
zone |
cloudflare_zone_bandwidth_ssl_encrypted_total |
counter |
zone |
cloudflare_zone_bandwidth_content_type_total |
counter |
zone, content_type |
cloudflare_zone_bandwidth_country_total |
counter |
zone, country |
Zone Threat Metrics
| Metric |
Type |
Labels |
cloudflare_zone_threats_total |
counter |
zone |
cloudflare_zone_threats_country_total |
counter |
zone, country |
cloudflare_zone_threats_type_total |
counter |
zone, type |
Zone Page/Unique Metrics
| Metric |
Type |
Labels |
cloudflare_zone_pageviews_total |
counter |
zone |
cloudflare_zone_uniques_total |
counter |
zone |
Colocation Metrics
| Metric |
Type |
Labels |
cloudflare_zone_colocation_visits_total |
counter |
zone, colo, host |
cloudflare_zone_colocation_edge_response_bytes_total |
counter |
zone, colo, host |
cloudflare_zone_colocation_requests_total |
counter |
zone, colo, host |
cloudflare_zone_colocation_error_visits_total |
counter |
zone, colo, host, status |
cloudflare_zone_colocation_error_edge_response_bytes_total |
counter |
zone, colo, host, status |
cloudflare_zone_colocation_error_requests_total |
counter |
zone, colo, host, status |
Firewall Metrics
| Metric |
Type |
Labels |
cloudflare_zone_firewall_events_total |
counter |
zone, action, source, rule, host, country |
cloudflare_zone_firewall_bots_detected_total |
counter |
zone, bot_score, detection_source |
Health Check Metrics
| Metric |
Type |
Labels |
cloudflare_zone_health_check_events_origin_total |
counter |
zone, health_status, origin_ip, region, fqdn, failure_reason |
cloudflare_zone_health_check_events_avg |
gauge |
zone |
cloudflare_zone_health_check_rtt_seconds |
gauge |
zone, origin_ip, fqdn |
cloudflare_zone_health_check_ttfb_seconds |
gauge |
zone, origin_ip, fqdn |
cloudflare_zone_health_check_tcp_connection_seconds |
gauge |
zone, origin_ip, fqdn |
cloudflare_zone_health_check_tls_handshake_seconds |
gauge |
zone, origin_ip, fqdn |
Worker Metrics
| Metric |
Type |
Labels |
cloudflare_worker_requests_total |
counter |
script_name |
cloudflare_worker_errors_total |
counter |
script_name |
cloudflare_worker_cpu_time_seconds |
gauge |
script_name, quantile |
cloudflare_worker_duration_seconds |
gauge |
script_name, quantile |
Load Balancer Metrics
| Metric |
Type |
Labels |
cloudflare_zone_pool_health_status |
gauge |
zone, lb_name, pool_name |
cloudflare_zone_pool_requests_total |
counter |
zone, lb_name, pool_name, origin_name |
cloudflare_zone_lb_pool_rtt_seconds |
gauge |
zone, lb_name, pool_name |
cloudflare_zone_lb_steering_policy_info |
gauge |
zone, lb_name, policy |
cloudflare_zone_lb_origins_selected_count |
gauge |
zone, lb_name, pool_name |
cloudflare_zone_lb_origin_weight |
gauge |
zone, lb_name, pool_name, origin_name |
Logpush Metrics
| Metric |
Type |
Labels |
cloudflare_logpush_failed_jobs_account_total |
counter |
account, job_id, status, destination_type |
cloudflare_logpush_failed_jobs_zone_total |
counter |
zone, job_id, destination_type |
Error Rate Metrics
| Metric |
Type |
Labels |
cloudflare_zone_customer_error_4xx_total |
counter |
zone, status, country, host |
cloudflare_zone_customer_error_5xx_total |
counter |
zone, status, country, host |
cloudflare_zone_edge_error_rate |
gauge |
zone |
cloudflare_zone_origin_error_rate |
gauge |
zone |
cloudflare_zone_origin_response_duration_seconds |
gauge |
zone, status, country, host |
Cache Metrics
| Metric |
Type |
Labels |
cloudflare_zone_cache_hit_ratio |
gauge |
zone |
| `cloudflare_zone_ca |
|
|