A self-hostable Prometheus exporter for Cloudflare metrics. Runs as a standalone Go binary with no runtime dependencies.
| 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_type |
cloudflare_zone_requests_ssl_protocol_total |
counter | zone, ssl_protocol |
cloudflare_zone_requests_http_version_total |
counter | zone, http_version |
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 |
cloudflare_zone_threats_total |
counter | zone |
cloudflare_zone_threats_country_total |
counter | zone, country |
cloudflare_zone_threats_type_total |
counter | zone, type |
cloudflare_zone_pageviews_total |
counter | zone |
cloudflare_zone_uniques_total |
counter | zone |
cloudflare_zone_cache_hit_ratio |
gauge | zone |
cloudflare_zone_firewall_events_total |
counter | zone, action, source, rule, host, country |
cloudflare_zone_firewall_bots_detected_total |
counter | zone, bot_score, detection_source |
cloudflare_zone_bot_requests_by_country_total |
counter | zone, country |
cloudflare_zone_customer_error_4xx_total |
counter | zone, status, country, host |
cloudflare_zone_customer_error_5xx_total |
counter | zone, status, country, host |
cloudflare_zone_origin_response_duration_seconds |
gauge | zone, status, country, host |
cloudflare_zone_origin_error_rate |
gauge | zone |
cloudflare_zone_requests_status_country_host_total |
counter | zone, edge_status, country, host |
cloudflare_zone_edge_error_rate |
gauge | zone |
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, status |
cloudflare_zone_colocation_requests_status_total |
counter | zone, colo, host, status |
cloudflare_zone_requests_method_total |
counter | zone, method |
cloudflare_zone_health_check_events_total |
counter | zone, health_status, origin_ip, region, fqdn, failure_reason |
cloudflare_zone_health_check_rtt_avg_ms |
gauge | zone, origin_ip, region, fqdn |
cloudflare_zone_health_check_ttfb_avg_ms |
gauge | zone, origin_ip, region, fqdn |
cloudflare_zone_lb_pool_requests_total |
counter | zone, lb, pool, origin, region |
cloudflare_zone_lb_pool_healthy |
gauge | zone, lb, pool |
cloudflare_zone_lb_pool_rtt_avg_ms |
gauge | zone, lb, pool |
cloudflare_zone_lb_origins_selected_total |
counter | zone, lb, steering_policy |
cloudflare_zone_logpush_failed_jobs_total |
counter | zone, job_id, status, destination_type |
cloudflare_zone_origin_status_total |
counter | zone, status, country, host |
cloudflare_zone_cache_miss_origin_response_duration_seconds |
gauge | zone, country, host |
| Metric | Type | Labels |
|---|---|---|
cloudflare_zone_ssl_certificate_expiry_timestamp |
gauge | zone, cert_id, type, issuer, status |
cloudflare_zone_lb_pool_origin_weight |
gauge | zone, lb, pool, origin, address |
All gauges — window snapshots fetched over 1h and 2h lookback windows from the current maxtime. The window label is 1h or 2h.
| Metric | Type | Labels |
|---|---|---|
cloudflare_zone_hostname_requests |
gauge | zone, host, window |
cloudflare_zone_hostname_requests_by_status |
gauge | zone, host, status, window |
cloudflare_zone_hostname_cache_status |
gauge | zone, host, cache_status, window |
cloudflare_zone_hostname_edge_ttfb_seconds |
gauge | zone, host, quantile, window |
cloudflare_zone_hostname_origin_response_duration_seconds |
gauge | zone, host, quantile, window |
| Metric | Type | Labels |
|---|---|---|
cloudflare_magic_transit_active_tunnels |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_healthy_tunnels |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_failures |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_edge_colo_count |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_failure_by_status |
gauge | account, tunnel_name, site_name, result_status |
cloudflare_magic_transit_tunnel_state_healthy |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_state_degraded |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_state_down |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_slo_status |
gauge | account, tunnel_name, site_name, status |
cloudflare_magic_transit_tunnel_effective_slo |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_target_slo |
gauge | account, tunnel_name, site_name |
cloudflare_magic_transit_tunnel_traffic_bits_total |
counter | account, tunnel_name, direction, on_ramp, off_ramp |
cloudflare_magic_transit_tunnel_traffic_packets_total |
counter | account, tunnel_name, direction, on_ramp, off_ramp |
cloudflare_magic_firewall_rule_bits_total |
counter | account, rule_id |
cloudflare_magic_firewall_rule_packets_total |
counter | account, rule_id |
| Metric | Type | Labels |
|---|---|---|
cloudflare_worker_requests_total |
counter | account, script_name |
cloudflare_worker_errors_total |
counter | account, script_name |
cloudflare_worker_cpu_time_seconds |
gauge | account, script_name, quantile |
cloudflare_worker_duration_seconds |
gauge | account, script_name, quantile |
cloudflare_logpush_failed_jobs_account_total |
counter | account, job_id, status, destination_type |
cloudflare_stream_video_playback_count_total |
counter | account, country, media_type |
cloudflare_stream_video_playback_time_viewed_seconds_total |
counter | account, country, media_type |
cloudflare_stream_live_input_segments_total |
counter | account, event_code |
cloudflare_stream_live_input_bit_rate_bps |
gauge | account, event_code |
cloudflare_stream_live_input_gop_duration_seconds |
gauge | account, event_code |
cloudflare_stream_live_input_upload_duration_ratio |
gauge | account, event_code |
cloudflare_network_analytics_magic_transit_bits_total |
counter | account, outcome, direction, ip_protocol, mitigation_system |
cloudflare_network_analytics_magic_transit_packets_total |
counter | account, outcome, direction, ip_protocol, mitigation_system |
cloudflare_network_analytics_magic_firewall_bits_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_magic_firewall_packets_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_dosd_bits_total |
counter | account, outcome, direction, ip_protocol, attack_vector |
cloudflare_network_analytics_dosd_packets_total |
counter | account, outcome, direction, ip_protocol, attack_vector |
cloudflare_network_analytics_idps_bits_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_idps_packets_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_tcp_protection_bits_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_tcp_protection_packets_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_dns_protection_bits_total |
counter | account, outcome, direction, ip_protocol |
cloudflare_network_analytics_dns_protection_packets_total |
counter | account, outcome, direction, ip_protocol |
| Metric | Type | Labels |
|---|---|---|
cloudflare_exporter_up |
gauge | — |
cloudflare_exporter_accounts_total |
gauge | — |
cloudflare_exporter_zones_total |
gauge | account |
cloudflare_exporter_errors_total |
counter | account, error_code |
All configuration is via environment variables. No config files.
| Variable | Default | Description |
|---|---|---|
CF_API_TOKEN |
required | Cloudflare API token with Analytics:Read scope |
CF_ACCOUNTS |
(all) | Comma-separated account ID allowlist |
CF_ZONES |
(all) | Comma-separated zone ID allowlist |
CF_FREE_TIER_ACCOUNTS |
— | Comma-separated account IDs to treat as free tier (skips paid-only GraphQL queries) |
METRIC_REFRESH_INTERVAL |
60s |
How often to fetch metrics from Cloudflare |
SCRAPE_DELAY |
5m |
Lookback offset to account for Cloudflare analytics ingestion lag |
TIME_WINDOW |
1m |
Size of each query window |
ACCOUNT_CACHE_TTL |
10m |
How often to refresh the account list |
ZONE_CACHE_TTL |
30m |
How often to refresh zone lists |
SSL_CACHE_TTL |
30m |
How often to refresh SSL certificate data |
HEALTH_CACHE_TTL |
10s |
Health check result cache duration |
METRICS_PATH |
/metrics |
Path to expose Prometheus metrics on |
METRICS_DENYLIST |
— | Comma-separated metric name prefixes to exclude from output |
CF_HTTP_STATUS_GROUP |
false |
Group HTTP status codes into 2xx/4xx/5xx buckets |
LISTEN_ADDR |
:8080 |
Address and port to listen on |
HOST_METRICS_ALLOWLIST |
— | Comma-separated list of hostnames (max 50) to collect per-host metrics for |
QUERY_LIMIT |
10000 |
Maximum rows per GraphQL query |
LOG_LEVEL |
info |
Log level: debug, info, warn, error |
LOG_FORMAT |
json |
Log format: json or text |
Create a token at My Profile → API Tokens with:
- Zone → Analytics → Read (for zone-level metrics)
- Account → Analytics → Read (for account-level metrics: Workers, Stream, Network Analytics)
- Zone → Zone → Read (for zone listing)
Scope the token to the accounts/zones you want to monitor.
docker run -e CF_API_TOKEN=your_token ghcr.io/aymanbagabas/cloudflare-prometheus-exporter:latestCF_API_TOKEN=your_token ./cloudflare-prometheus-exporterapiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflare-exporter
spec:
replicas: 1
selector:
matchLabels:
app: cloudflare-exporter
template:
metadata:
labels:
app: cloudflare-exporter
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
containers:
- name: exporter
image: ghcr.io/aymanbagabas/cloudflare-prometheus-exporter:latest
ports:
- containerPort: 8080
env:
- name: CF_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-exporter
key: api-token
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 200m
memory: 128Mimake build # produces ./cloudflare-prometheus-exporter binary
make docker # builds Docker image
make run # go run .| Path | Description |
|---|---|
GET /metrics |
Prometheus metrics (text format) |
GET /health |
JSON health check — checks Cloudflare API and GraphQL reachability |
Zone-level GraphQL queries (httpRequestsAdaptiveGroups, etc.) require a paid Cloudflare plan. Free zones are automatically detected via plan ID and skipped for those queries. SSL certificate and load balancer weight metrics (REST-based) work on all plans.
If your account is entirely on the free plan, add it to CF_FREE_TIER_ACCOUNTS to skip account-level queries that require paid features (Stream, etc.).
MIT — see LICENSE.