EC2→Internet egress at US$0.09/GB: why CloudFront flat-rate beats Cloudflare for AWS origins
Published on June 29, 2026

The problem: direct egress is AWS's invisible cost
Servers delivering content (media, PDFs, heavy assets) straight to the internet pay AWS egress at US$0.09/GB. On a fleet pushing ~12.5 TB/month, that's ~US$1,124/month — a line that often hides inside "EC2-Other" with nobody attributing it to a cause.
The architectural key: traffic from EC2 to CloudFront in the SAME region is US$0. You only pay CloudFront's egress — and that's where the flat-rate model comes in.
The flat-rate vs per-GB math
CloudFront's flat-rate plans charge a fixed amount for a large transfer allowance, instead of cents per GB:
Direct EC2→Internet egress: 12.5 TB × US$0.09/GB ≈ US$1,124/mo
CloudFront (intra-region EC2 origin): EC2→CF = US$0 + CF→Internet flat-rate
Flat-rate plans (order of magnitude):
Free ~US$0 (small allowance)
Pro ~US$15 (~50 TB)
Business ~US$200
Premium ~US$1000
Cloudflare for ~50 TB from an AWS origin: ~US$25 (plan) + ~US$4,500 AWS egress
(AWS egress OUT stays at US$0.09/GB)The Cloudflare gotcha in front of an AWS origin: you swap to a cheap plan but keep paying AWS→Cloudflare egress at US$0.09/GB. CloudFront, being AWS's own, zeroes that leg.
Measure per-instance egress (Cost Explorer misleads)
Cost Explorer aggregates egress in a way that makes per-instance attribution hard. The reliable measurement is CloudWatch's NetworkOut metric, per instance. A boto3 script does it — with 4 real gotchas that cost time:
import boto3
# Gotcha 1: the client region must be the instance's region (metric is regional)
cw = boto3.client("cloudwatch", region_name="sa-east-1")
# Gotcha 2: Period has limits — 30 days in 1 datapoint (2592000) can come back EMPTY;
# use smaller granularity and sum, or Period=86400 (daily).
# Gotcha 3: pagination — without a paginator you lose instances past the 1st page.
ec2 = boto3.client("ec2", region_name="sa-east-1")
paginator = ec2.get_paginator("describe_instances")
for page in paginator.paginate():
for r in page["Reservations"]:
for i in r["Instances"]:
iid = i["InstanceId"]
m = cw.get_metric_statistics(
Namespace="AWS/EC2", MetricName="NetworkOut",
Dimensions=[{"Name": "InstanceId", "Value": iid}],
StartTime="2026-02-01T00:00:00Z", EndTime="2026-03-01T00:00:00Z",
Period=86400, Statistics=["Sum"])
total_gb = sum(d["Sum"] for d in m["Datapoints"]) / (1024**3)
print(iid, round(total_gb, 1), "GB")
# Gotcha 4: on PEP 668 environments, installing boto3 needs --break-system-packagesNetworkOut in bytes, summed over the month and converted to GB, gives real per-instance egress — the basis for deciding who goes behind CloudFront first.
Lessons
1. EC2→CloudFront intra-region is US$0. That's why CloudFront flat-rate wins for AWS origins: you don't pay the origin→CDN leg.
2. A cheap Cloudflare plan doesn't zero AWS egress. An AWS origin keeps paying US$0.09/GB to Cloudflare. Do the full math, not just the plan price.
3. Measure with NetworkOut, not Cost Explorer. The CloudWatch metric attributes egress per instance; Cost Explorer aggregates and hides the source.
Conclusion
Egress is the cost that grows unnoticed because it dilutes into aggregate lines. By measuring NetworkOut per instance and putting the biggest emitters behind a CloudFront flat-rate plan, ~US$1,124/month of variable egress becomes a fixed, predictable cost — without switching providers, using the arithmetic AWS itself favors.