← Back to DevBytes

Monitoring CloudFront: Metrics, Alarms, and Dashboards

Introduction to CloudFront Monitoring

Amazon CloudFront is a fast content delivery network (CDN) that serves content to end users with low latency. But delivering content globally is only half the story — you need deep visibility into how your distribution is performing, where bottlenecks occur, and whether your origin infrastructure is healthy. Monitoring CloudFront involves collecting metrics, setting alarms on critical thresholds, and building dashboards that give you a real-time operational picture. This tutorial walks you through every aspect, from the core metrics available to production-grade alarm configurations and dashboard design patterns.

What CloudFront Monitoring Actually Measures

CloudFront emits metrics to Amazon CloudWatch in two categories: distribution-level metrics (published by CloudFront itself) and origin-level metrics (published by the origin service, such as an Application Load Balancer or S3 bucket). Distribution metrics are further split into real-time logs and standard CloudWatch metrics. Standard metrics are available at no extra cost and include request counts, data transfer, error rates, and cache behavior. Real-time metrics provide granular, second-by-second visibility but require enabling real-time logs first.

Core Distribution Metrics

The following metrics are published automatically to CloudWatch in the AWS/CloudFront namespace once you enable the CloudFront integration:

Per-Origin Metrics

CloudFront also publishes metrics grouped by origin domain name. These appear with dimensions like DistributionId and Origin and let you pinpoint exactly which origin is causing errors. For example, if you have a multi-origin setup with an S3 bucket and a custom API origin, you can isolate 5xx errors to the API origin specifically.

Real-Time Metrics (Near Real-Time)

When you enable real-time logs, CloudFront sends log records to a Kinesis Data Stream within seconds of request completion. These logs feed into CloudWatch metrics via a subscription, giving you per-second granularity. Real-time metrics are invaluable for detecting anomalies during deployments, traffic surges, or DDoS-like patterns that standard 1-minute metrics might smooth out.

Why Monitoring CloudFront Matters

Without proper monitoring, you are flying blind. Here's what's at stake:

How to Enable CloudFront Monitoring Step by Step

CloudFront metrics are not enabled by default for all distributions. You must explicitly turn on the CloudFront-CloudWatch integration. Here is the complete process using the AWS CLI and console approaches.

Step 1: Enable CloudWatch Metrics for a Distribution

You can enable metrics via the CloudFront console under the distribution's Monitoring tab, or programmatically. The following AWS CLI command enables standard monitoring for an existing distribution:

aws cloudfront update-distribution \
  --id E1EXAMPLE123 \
  --if-match "ETag-from-get-distribution" \
  --distribution-config file://distribution-config.json

The distribution-config.json file must include the MonitoringSubscription element. Here is a minimal example:

{
  "CallerReference": "monitoring-update-2025-01-01",
  "Aliases": {
    "Quantity": 1,
    "Items": ["cdn.example.com"]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "my-origin",
    "ViewerProtocolPolicy": "redirect-to-https",
    "MinTTL": 0,
    "DefaultTTL": 86400,
    "MaxTTL": 31536000,
    "ForwardedValues": {
      "QueryString": false,
      "Cookies": { "Forward": "none" }
    },
    "TrustedSigners": {
      "Enabled": false,
      "Quantity": 0
    },
    "LambdaFunctionAssociations": {
      "Quantity": 0
    }
  },
  "Origins": {
    "Quantity": 1,
    "Items": [
      {
        "Id": "my-origin",
        "DomainName": "my-bucket.s3.amazonaws.com",
        "S3OriginConfig": {
          "OriginAccessIdentity": ""
        }
      }
    ]
  },
  "PriceClass": "PriceClass_100",
  "Enabled": true,
  "MonitoringSubscription": {
    "MonitoringSubscription": {
      "RealtimeMetricsSubscriptionConfig": {
        "RealtimeMetricsSubscriptionStatus": "Enabled"
      }
    }
  }
}

Note: You must first retrieve the distribution's current configuration with aws cloudfront get-distribution --id E1EXAMPLE123 and use the returned ETag value for the --if-match parameter. The configuration above enables real-time metrics; for standard metrics only, omit the MonitoringSubscription block or set RealtimeMetricsSubscriptionStatus to Disabled.

Step 2: Verify Metrics Are Flowing

Within a few minutes, navigate to CloudWatch Metrics in the AWS console, select the AWS/CloudFront namespace, and look for metrics with your distribution ID. You can also list them via CLI:

aws cloudwatch list-metrics \
  --namespace AWS/CloudFront \
  --dimensions Name=DistributionId,Value=E1EXAMPLE123

Expected output resembles:

{
  "Metrics": [
    {
      "Namespace": "AWS/CloudFront",
      "MetricName": "Requests",
      "Dimensions": [
        { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
        { "Name": "Region", "Value": "Global" }
      ]
    },
    {
      "Namespace": "AWS/CloudFront",
      "MetricName": "BytesDownloaded",
      "Dimensions": [
        { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
        { "Name": "Region", "Value": "Global" }
      ]
    },
    {
      "Namespace": "AWS/CloudFront",
      "MetricName": "4xxErrorRate",
      "Dimensions": [
        { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
        { "Name": "Region", "Value": "Global" }
      ]
    }
  ]
}

Step 3: Retrieve Metric Data Programmatically

Use get-metric-data to pull time-series data. The following query retrieves the 5xx error rate for the last 3 hours at 5-minute intervals:

aws cloudwatch get-metric-data \
  --metric-data-queries '[
    {
      "Id": "m1",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/CloudFront",
          "MetricName": "5xxErrorRate",
          "Dimensions": [
            { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
            { "Name": "Region", "Value": "Global" }
          ]
        },
        "Period": 300,
        "Stat": "Average"
      },
      "ReturnData": true
    }
  ]' \
  --start-time "$(date -u -d '3 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)"

The response contains timestamped data points you can feed into your own tooling or scripts.

Building CloudWatch Alarms for CloudFront

Alarms turn passive metrics into actionable notifications. CloudFront metrics support standard CloudWatch alarms with all the usual features: static thresholds, anomaly detection, composite alarms, and actions like SNS notifications or Auto Scaling (though scaling applies to origins, not CloudFront itself).

Designing an Error Rate Alarm

A production-grade alarm should be based on the 5xxErrorRate metric, evaluated over a window long enough to avoid flapping but short enough to catch real issues. A 5-minute evaluation period with a threshold of 1% is a reasonable starting point. Here's how to create it via CLI:

aws cloudwatch put-metric-alarm \
  --alarm-name "cloudfront-5xx-error-rate-high" \
  --alarm-description "Triggers when 5xx error rate exceeds 1% for 2 consecutive periods" \
  --namespace "AWS/CloudFront" \
  --metric-name "5xxErrorRate" \
  --dimensions "Name=DistributionId,Value=E1EXAMPLE123" "Name=Region,Value=Global" \
  --statistic "Average" \
  --period 300 \
  --evaluation-periods 2 \
  --threshold 1.0 \
  --comparison-operator "GreaterThanThreshold" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:cloudfront-alerts" \
  --ok-actions "arn:aws:sns:us-east-1:123456789012:cloudfront-alerts" \
  --treat-missing-data "notBreaching"

Key parameters explained:

Cache Hit Rate Alarm

A falling cache hit rate often precedes origin overload. Create an alarm that triggers when the hit rate drops below a baseline:

aws cloudwatch put-metric-alarm \
  --alarm-name "cloudfront-cache-hit-rate-low" \
  --alarm-description "Triggers when cache hit rate drops below 80% for 10 minutes" \
  --namespace "AWS/CloudFront" \
  --metric-name "CacheHitRate" \
  --dimensions "Name=DistributionId,Value=E1EXAMPLE123" "Name=Region,Value=Global" \
  --statistic "Average" \
  --period 300 \
  --evaluation-periods 2 \
  --threshold 80.0 \
  --comparison-operator "LessThanThreshold" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:cloudfront-alerts"

Composite Alarm for Holistic Health

Combine multiple alarms into a single composite alarm that triggers only when both error rate and cache hit rate are unhealthy. This reduces noise — a lone cache hit drop during off-peak hours might be harmless, but combined with elevated errors it demands immediate attention:

aws cloudwatch put-composite-alarm \
  --alarm-name "cloudfront-composite-health-failure" \
  --alarm-description "Triggers when both 5xx errors are high AND cache hit rate is low" \
  --alarm-rule "ALARM(cloudfront-5xx-error-rate-high) AND ALARM(cloudfront-cache-hit-rate-low)" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:pagerduty-integration"

Composite alarms can reference any number of underlying metric alarms and support AND / OR logic with nested expressions.

Anomaly Detection Alarms

For metrics with seasonal patterns (e.g., traffic that peaks at 9 AM and dips at midnight), static thresholds don't work well. CloudWatch anomaly detection learns the expected band and triggers when values deviate significantly:

aws cloudwatch put-metric-alarm \
  --alarm-name "cloudfront-requests-anomaly" \
  --namespace "AWS/CloudFront" \
  --metric-name "Requests" \
  --dimensions "Name=DistributionId,Value=E1EXAMPLE123" "Name=Region,Value=Global" \
  --statistic "Sum" \
  --period 300 \
  --evaluation-periods 2 \
  --threshold 2.0 \
  --comparison-operator "GreaterThanUpperThreshold" \
  --treat-missing-data "missing" \
  --threshold-metric-id "ad1" \
  --metrics '[
    {
      "Id": "ad1",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/CloudFront",
          "MetricName": "Requests",
          "Dimensions": [
            { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
            { "Name": "Region", "Value": "Global" }
          ]
        },
        "Period": 300,
        "Stat": "Sum"
      },
      "ReturnData": false
    },
    {
      "Id": "m1",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/CloudFront",
          "MetricName": "Requests",
          "Dimensions": [
            { "Name": "DistributionId", "Value": "E1EXAMPLE123" },
            { "Name": "Region", "Value": "Global" }
          ]
        },
        "Period": 300,
        "Stat": "Sum"
      },
      "ReturnData": true
    }
  ]' \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:cloudfront-alerts"

The anomaly model (ad1) trains on up to two weeks of data and sets upper and lower bands. The GreaterThanUpperThreshold comparison triggers when actual requests exceed the upper band by the threshold factor (2.0 means double the expected value).

Creating CloudFront Dashboards in CloudWatch

Dashboards give you a single pane of glass for all CloudFront metrics. You can build them through the console, via CLI, or programmatically with CloudFormation. A well-designed dashboard groups related widgets, uses consistent time ranges, and surfaces both real-time and trend data.

Building a Dashboard via AWS CLI

The following script creates a complete CloudFront monitoring dashboard with request counts, error rates, cache hit rate, and data transfer widgets:

aws cloudwatch put-dashboard \
  --dashboard-name "CloudFront-Operations" \
  --dashboard-body '{
  "widgets": [
    {
      "type": "text",
      "x": 0,
      "y": 0,
      "width": 24,
      "height": 2,
      "properties": {
        "markdown": "## CloudFront Distribution E1EXAMPLE123 — Real-Time Health\n*Last updated: ${DATE}*\n**Alarms:** 5xxErrorRate > 1% | CacheHitRate < 80%"
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 2,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "Request Count (5 min)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Sum",
        "metrics": [
          [ "AWS/CloudFront", "Requests", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 8,
      "y": 2,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "5xx Error Rate (%)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Average",
        "metrics": [
          [ "AWS/CloudFront", "5xxErrorRate", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ],
        "annotations": {
          "horizontal": [
            {
              "label": "Alarm threshold 1%",
              "value": 1,
              "color": "#ff0000"
            }
          ]
        }
      }
    },
    {
      "type": "metric",
      "x": 16,
      "y": 2,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "4xx Error Rate (%)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Average",
        "metrics": [
          [ "AWS/CloudFront", "4xxErrorRate", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 8,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "Cache Hit Rate (%)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Average",
        "metrics": [
          [ "AWS/CloudFront", "CacheHitRate", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ],
        "annotations": {
          "horizontal": [
            {
              "label": "Min acceptable 80%",
              "value": 80,
              "color": "#ff9900"
            }
          ]
        }
      }
    },
    {
      "type": "metric",
      "x": 12,
      "y": 8,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "Bytes Downloaded (GB)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Sum",
        "metrics": [
          [ "AWS/CloudFront", "BytesDownloaded", "DistributionId", "E1EXAMPLE123", "Region", "Global", { "label": "Bytes Downloaded" } ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 14,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "Bytes Uploaded",
        "region": "us-east-1",
        "period": 300,
        "stat": "Sum",
        "metrics": [
          [ "AWS/CloudFront", "BytesUploaded", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 12,
      "y": 14,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "title": "Total Error Rate (%)",
        "region": "us-east-1",
        "period": 300,
        "stat": "Average",
        "metrics": [
          [ "AWS/CloudFront", "TotalErrorRate", "DistributionId", "E1EXAMPLE123", "Region", "Global" ]
        ]
      }
    },
    {
      "type": "alarm",
      "x": 0,
      "y": 20,
      "width": 24,
      "height": 4,
      "properties": {
        "title": "Active Alarms",
        "region": "us-east-1",
        "alarms": [
          "cloudfront-5xx-error-rate-high",
          "cloudfront-cache-hit-rate-low",
          "cloudfront-composite-health-failure"
        ]
      }
    }
  ]
}'

This dashboard places the most critical metrics — error rates and cache hit rate — prominently, includes threshold annotations for quick visual reference, and adds an alarm widget at the bottom to show current alarm states. The layout uses a 24-column grid; widgets at x=0..7 span 8 columns, x=8..15 span 8, and x=16..23 span 8.

Adding Origin-Specific Widgets

If your distribution has multiple origins, add dedicated widgets filtered by the Origin dimension. For example, to monitor 5xx errors for an API origin named api-origin:

{
  "type": "metric",
  "x": 0,
  "y": 24,
  "width": 12,
  "height": 6,
  "properties": {
    "view": "timeSeries",
    "stacked": false,
    "title": "5xx Error Rate — API Origin",
    "region": "us-east-1",
    "period": 300,
    "stat": "Average",
    "metrics": [
      [ "AWS/CloudFront", "5xxErrorRate", "DistributionId", "E1EXAMPLE123", "Region", "Global", "Origin", "api-origin" ]
    ]
  }
}

This granularity is essential for multi-origin architectures where one failing origin can be masked by healthy ones in aggregate metrics.

Infrastructure as Code: CloudFormation Template for Full Monitoring Stack

For production environments, you should provision monitoring resources alongside your CloudFront distribution. The following CloudFormation template snippet creates a distribution with monitoring enabled, an SNS topic for alerts, and a comprehensive set of CloudWatch alarms:

Resources:
  # SNS Topic for alarm notifications
  CloudFrontAlertTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: "CloudFront Alerts"
      Subscription:
        - Endpoint: "ops-team@example.com"
          Protocol: "email"
        - Endpoint: "arn:aws:lambda:us-east-1:123456789012:function:PagerDutySender"
          Protocol: "lambda"

  # CloudFront Distribution with monitoring enabled
  MyCDN:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        PriceClass: PriceClass_100
        Aliases:
          - cdn.example.com
        Origins:
          - Id: s3-origin
            DomainName: my-static-assets.s3.amazonaws.com
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${MyOAI}"
          - Id: api-origin
            DomainName: api.example.com
            CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols:
                - TLSv1.2
        DefaultCacheBehavior:
          TargetOriginId: s3-origin
          ViewerProtocolPolicy: redirect-to-https
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
          MinTTL: 0
          DefaultTTL: 86400
          MaxTTL: 31536000
        CacheBehaviors:
          - PathPattern: "/api/*"
            TargetOriginId: api-origin
            ViewerProtocolPolicy: redirect-to-https
            ForwardedValues:
              QueryString: true
              Cookies:
                Forward: all
              Headers:
                - Authorization
            MinTTL: 0
            DefaultTTL: 0
            MaxTTL: 300
        MonitoringSubscription:
          MonitoringSubscription:
            RealtimeMetricsSubscriptionConfig:
              RealtimeMetricsSubscriptionStatus: Enabled

  # CloudWatch Alarms
  High5xxAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: "cloudfront-5xx-error-rate-high"
      AlarmDescription: "5xx error rate exceeds 1% for two consecutive periods"
      Namespace: "AWS/CloudFront"
      MetricName: "5xxErrorRate"
      Dimensions:
        - Name: DistributionId
          Value: !Ref MyCDN
        - Name: Region
          Value: Global
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 1.0
      ComparisonOperator: GreaterThanThreshold
      AlarmActions:
        - !Ref CloudFrontAlertTopic
      OKActions:
        - !Ref CloudFrontAlertTopic
      TreatMissingData: notBreaching

  LowCacheHitRateAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: "cloudfront-cache-hit-rate-low"
      AlarmDescription: "Cache hit rate drops below 80%"
      Namespace: "AWS/CloudFront"
      MetricName: "CacheHitRate"
      Dimensions:
        - Name: DistributionId
          Value: !Ref MyCDN
        - Name: Region
          Value: Global
      Statistic: Average
      Period: 300
      EvaluationPeriods: 3
      Threshold: 80.0
      ComparisonOperator: LessThanThreshold
      AlarmActions:
        - !Ref CloudFrontAlertTopic

  High4xxAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: "cloudfront-4xx-error-rate-high"
      AlarmDescription: "4xx error rate exceeds 5% — possible auth or URL issues"
      Namespace: "AWS/CloudFront"
      MetricName: "4xxErrorRate"
      Dimensions:
        - Name: DistributionId
          Value: !Ref MyCDN
        - Name: Region
          Value: Global
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 5.0
      ComparisonOperator: GreaterThanThreshold
      AlarmActions:
        - !Ref CloudFrontAlertTopic

  CompositeHealthAlarm:
    Type: AWS::CloudWatch::CompositeAlarm
    Properties:
      AlarmName: "cloudfront-composite-health-failure"
      AlarmDescription: "Both 5xx errors high AND cache hit rate low"
      AlarmRule: !Sub "ALARM(${High5xxAlarm}) AND ALARM(${LowCacheHitRateAlarm})"
      AlarmActions:
        - !Ref CloudFrontAlertTopic

  # CloudWatch Dashboard
  CloudFrontDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: "CloudFront-Operations"
      DashboardBody: !Sub |
        {
          "widgets": [
            {
              "type": "text",
              "x": 0, "y": 0, "width": 24, "height": 2,
              "properties": {
                "markdown": "## CloudFront ${MyCDN}\n**Region:** Global | **Period:** 5 min"
              }
            },
            {
              "type": "metric",
              "x": 0, "y": 2, "width": 8, "height": 6,
              "properties": {
                "view": "timeSeries", "stacked": false,
                "title": "Requests (5 min)",
                "period": 300, "stat": "Sum",
                "metrics": [
                  [ "AWS/CloudFront", "Requests", "DistributionId", "${MyCDN}", "Region", "Global" ]
                ]
              }
            },
            {
              "type": "metric",
              "x": 8, "y": 2, "width": 8, "height": 6,
              "properties": {
                "view": "timeSeries", "stacked": false,
                "title": "5xx Error Rate (%)",
                "period": 300, "stat": "Average",
                "metrics": [
                  [ "AWS/CloudFront", "5xxErrorRate", "DistributionId", "${MyCDN}", "Region", "Global" ]
                ],
                "annotations": {
                  "horizontal": [
                    { "label": "Threshold 1%", "value": 1, "color": "#ff0000" }
                  ]
                }
              }
            },
            {
              "type": "metric",
              "x": 16, "y": 2, "width": 8, "height": 6,
              "properties": {
                "view": "timeSeries", "stacked": false,
                "title": "Cache Hit Rate (%)",
                "period": 300, "stat": "Average",
                "metrics": [
                  [ "AWS/CloudFront", "CacheHitRate", "DistributionId", "${MyCDN}", "Region", "Global" ]
                ],
                "annotations": {
                  "horizontal": [
                    { "label": "Min 80%", "value": 80, "color": "#ff9900" }
                  ]
                }
              }
            },
            {
              "type": "metric",
              "x": 0, "y": 8, "width": 12, "height": 6,
              "properties": {
                "view": "timeSeries", "stacked": false,
                "title": "Bytes Downloaded",
                "period": 300, "stat": "Sum",
                "metrics": [
                  [ "AWS/CloudFront", "BytesDownloaded", "DistributionId", "${MyCDN}", "Region", "Global" ]
                ]
              }
            },
            {
              "type": "metric",
              "x": 12, "y": 8, "width": 12, "height": 6,
              "properties": {
                "view": "timeSeries", "stacked": false,
                "title": "Total Error Rate (%)",
                "period": 300, "stat": "Average",
                "metrics": [
                  [ "AWS/CloudFront", "TotalErrorRate", "DistributionId", "${MyCDN}", "Region", "Global" ]
                ]
              }
            },
            {
              "type": "alarm",
              "x": 0, "y": 14, "width": 24, "height": 4,
              "properties": {
                "title": "Alarm States",
                "alarms": [
                  "cloudfront-5xx-error-rate-high",
                  "cloudfront-cache-hit-rate-low",
                  "cloudfront-composite-health-failure"
                ]
              }
            }
          ]
        }

This template gives you a fully reproducible monitoring stack. Deploy it with aws cloudformation deploy --template-file template.yaml --stack-name cloudfront-monitoring and every new distribution environment inherits the same alarms and dashboard.

Advanced: Using CloudFront Real-Time Logs for Custom Metrics

Standard CloudFront metrics aggregate at one-minute granularity. For sub-second visibility, enable real-time logs. These logs stream to Kinesis Data Streams, which you can process with Lambda to emit custom CloudWatch metrics. This is particularly useful for tracking business-specific KPIs like per-customer bandwidth, geo-specific error rates, or cache hit ratios by path pattern.

Enabling Real-Time Logs via CLI

aws cloudfront create-realtime-log-config \
  --name "production-real-time-logs" \
  --sampling-rate 100 \
  --stream-type "KinesisStream" \
  --kinesis-stream-config "arn:aws:kinesis:us-east-1:123456789012:stream/cloudfront-realtime-logs" \
  --fields "timestamp,distribution_id,request_id,status,request_protocol,request_method,request_uri,edge_location,origin_fetched,cache_hit,bytes_sent,user_agent"

Then attach the log config to your distribution:

aws cloudfront update-distribution \
  --id E1EXAMPLE123 \
  --if-match "current-etag" \
  --distribution-config file://config-with-real-time-logs.json

Lambda Processor for Custom Metrics

A Lambda function consuming the Kinesis stream can emit custom metrics with any dimensions you need. Here is a Python example that emits per-edge-location error counts:

import boto3
import json
import base64
import gzip
from datetime import datetime

cloudwatch = boto3.client('cloudwatch')

def process_records(records):
    for record in records:
        # CloudFront real-time log records are base64-encoded and gzipped
        decoded = base64.b64decode(record['kinesis']['data'])
        decompressed = gzip.decompress(decoded).decode('utf-8')
        
        # Each line is a JSON object
        for line in decompressed.strip().split('\n'):
            log_entry = json.loads(line)
            status_code = int(log_entry.get('status', 200))
            edge_location = log_entry.get('edge_location', 'unknown')
            distribution_id = log_entry.get('distribution_id', 'unknown')
            
            # Emit custom metric for 5xx errors by edge location
            if status_code >= 500:
                cloudwatch.put_metric_data(
                    Namespace='CloudFront/Custom',
                    MetricData=[{
                        'MetricName': 'Edge5xxErrors',
                        'Dimensions': [
                            {'Name': 'DistributionId', '

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles