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:
- Requests — Total number of HTTP requests handled by the distribution. Useful for traffic trend analysis and capacity planning.
- BytesDownloaded — Total bytes served to end users. Tracks data transfer out, which directly impacts billing.
- BytesUploaded — Total bytes uploaded from clients to CloudFront (e.g., POST requests, file uploads).
- 4xxErrorRate — Percentage of requests that resulted in a client error (4xx status code). A sudden spike often indicates misconfigured origins, expired tokens, or bad URLs.
- 5xxErrorRate — Percentage of requests resulting in a server error (5xx). Critical for detecting origin failures or Lambda@Edge timeouts.
- CacheHitRate — The ratio of requests served from cache versus forwarded to origin. Lower-than-expected hit rates mean more origin load and higher latency.
- TotalErrorRate — Combined 4xx and 5xx error percentage. A single high-level health indicator.
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:
- User Experience — High error rates or latency directly degrade the experience. Monitoring alerts you before users complain.
- Cost Control — Cache hit rates directly impact origin load and data transfer costs. A 10% drop in cache hit rate can double your origin infrastructure costs.
- Security Posture — Abnormal request patterns (e.g., a sudden 50x spike in 4xx errors) may indicate probing or exploitation attempts.
- Deployment Safety — When you deploy new origins or update cache policies, monitoring tells you immediately if something broke.
- Compliance & SLAs — If you guarantee 99.9% availability, you need metrics to prove it and alarms to catch breaches.
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:
--period 300— Evaluate in 5-minute blocks. Shorter periods increase cost and noise; longer periods delay detection.--evaluation-periods 2— Require two consecutive breaching periods before alarming. This prevents transient spikes from triggering false positives.--treat-missing-data notBreaching— If data stops arriving (e.g., distribution disabled), the alarm stays OK rather than going into an indeterminate state.
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', '