Data transfer is the one charge almost nobody models up front and almost everybody overpays for. It is not one price — it is at least five different prices, billed per gigabyte, scattered across hundreds of line items, and largely invisible in a top-line glance. This is the definitive, neutral map: every transfer charge AWS levies, the exact per-GB numbers, why the bill silently balloons, how to read it in the Cost and Usage Report, and the specific fixes that cut it — most of them free to implement.
Compute and storage are intuitive: you run an instance for an hour, you store a gigabyte for a month, you pay. Data transfer breaks that intuition because the price depends on where the bytes came from, where they went, and which AWS component they passed through on the way — and the bill never states those answers plainly.
On a typical AWS invoice, compute and storage each appear as a small number of large, legible line items. Data transfer does not. It fragments into dozens of entries with names like "USE1-USE2-AWS-Out-Bytes", "EU-DataTransfer-Regional-Bytes", and "USE1-NatGateway-Bytes", each priced per gigabyte, each tied to a specific direction and pair of locations. There is no single row labelled "data transfer: $4,200." The $4,200 is smeared across the bill, which is exactly why it survives so many cost reviews untouched.
The deeper reason it confuses people is that AWS charges for movement, and movement is directional and relational. Storing 1 TB in S3 has one price. Moving that 1 TB out to the internet, or to another region, or across Availability Zones to an instance, or through a NAT Gateway, each has a different price — and some of those movements are billed on both ends. The mental model most engineers carry ("data is basically free to move around inside the cloud") is simply wrong, and the bill is the only place that corrects them, in a language designed for accountants rather than architects.
There is also a genuine asymmetry that trips people up: data coming INTO AWS from the internet (ingress) is almost always free, while data going OUT (egress) is the expensive direction. This is deliberate. Free ingress makes it frictionless to move your data and traffic onto AWS; metered egress makes it costly to move large volumes back off, or to serve large volumes out to users without using AWS's own delivery services. Understanding that asymmetry is the first step to understanding the whole pricing map.
Finally, the numbers are small per unit — cents and fractions of a cent per gigabyte — which fools teams into ignoring them. But cloud systems move staggering volumes: a single analytics pipeline can shuttle tens of terabytes a day. At $0.01–$0.09 per gigabyte, terabytes add up fast, and because no individual line item looks alarming, the total hides in plain sight. The rest of this guide makes every one of those prices explicit.
There are five categories of data-transfer charge that account for nearly every transfer dollar on a standard AWS bill. Here is each one, what triggers it, and the real per-gigabyte price in US regions (other regions vary, but the structure is identical).
Prices below are the standard published US-region rates as of 2026 and are tiered or flat as noted. Treat them as the structure to reason with; always confirm the exact figure for your region and service in the AWS pricing pages, because a handful of regions (notably some in South America and Asia-Pacific) carry meaningfully higher egress rates.
What triggers it: any byte leaving AWS for the public internet — an API response to a browser, a file download, a video stream, a webhook you send out, a database replicated to something off-cloud.
Price: the first 100 GB/month aggregated across most services is free. After that the standard rate starts at ~$0.09/GB for the first 10 TB/month, then tiers down: ~$0.085/GB for the next 40 TB, ~$0.07/GB for the next 100 TB, and lower again above 150 TB. Most startups live entirely in the $0.09 tier.
The asymmetry: ingress from the internet is $0.00. You are only ever billed for the outbound direction.
Why it matters: for any product that serves media, large API payloads, or downloads directly from EC2 or an Application Load Balancer, this is usually the single largest transfer line — and the one CloudFront is designed to cut.
What triggers it: traffic between resources in different Availability Zones within the same region — an EC2 instance in us-east-1a talking to an RDS replica in us-east-1b, or a Kubernetes pod calling a service that happened to schedule in another AZ.
Price: $0.01/GB in EACH direction. Send a gigabyte from AZ-a to AZ-b and get a response back and you have paid $0.02/GB for the round trip. This per-direction billing is the single most misunderstood number in AWS networking.
Why it is silent: AZ placement is often invisible to developers. A service mesh, a managed Kubernetes cluster, or an auto-scaling group spreads workloads across AZs for resilience — which is correct for availability and quietly expensive for a chatty east-west traffic pattern.
The trade-off: you cross AZs for fault tolerance. Collapsing everything into one AZ removes the charge but removes the resilience too. The fix is selective, not absolute — keep the chattiest pairs co-located, keep the rest spread.
What triggers it: data moving from a resource in one region to a resource in another — cross-region replication of an S3 bucket, a global database, an analytics job in us-east-1 reading from a data store in eu-west-1, or a multi-region active-active deployment syncing state.
Price: typically ~$0.02/GB between US regions, with higher rates out of certain regions (transfer out of some Asia-Pacific and South America regions runs $0.08–$0.16/GB). Billed on the source side as "out."
Why it matters: teams adopt multi-region for latency or compliance and then discover the replication traffic is a standing monthly charge proportional to write volume. Cross-region analytics — querying data that lives elsewhere — is the other common surprise.
What triggers it: any traffic from private-subnet resources that routes through a NAT Gateway to reach the internet OR, critically, to reach AWS services like S3 when no VPC endpoint exists.
Price: a NAT Gateway costs ~$0.045/hour just to exist (~$32/month each), PLUS $0.045/GB of data processed through it. The processing charge stacks on top of any egress charge for the same bytes — so internet-bound traffic through NAT can effectively cost $0.045 (processing) + $0.09 (egress) = ~$0.135/GB.
The classic waste: EC2 instances in a private subnet pulling from or pushing to S3, with traffic routed through the NAT Gateway because nobody added an S3 Gateway endpoint. Every gigabyte to S3 incurs $0.045/GB of NAT processing for nothing — a Gateway endpoint would make that traffic free.
What triggers it: traffic through Interface VPC endpoints (PrivateLink), data processed by Application/Network Load Balancers (LCU charges include a processed-bytes dimension), and assorted intra-region service-to-service transfer.
Price: Interface endpoints carry ~$0.01/hour per endpoint per AZ plus ~$0.01/GB processed; load balancers bill processed bytes as part of their capacity-unit pricing. Individually small, collectively meaningful at scale.
Why it matters: these are the "long tail" of transfer charges. They rarely dominate, but on a mature architecture with many endpoints and load balancers they aggregate into a noticeable slice that is easy to overlook.
Data into AWS is almost always free. Data out, data across AZs, data across regions, and data through a NAT Gateway all cost money — and cross-AZ is billed in both directions. When you cannot remember a specific rate, reason from this: movement is metered, and the expensive movements are out, across, and through.
Transfer costs do not spike the way a runaway EC2 fleet does. They creep, because the architectural decisions that generate them are made for reasons that have nothing to do with cost, by people who never see the bill.
The first mechanism is fragmentation. Because transfer is billed per gigabyte across hundreds of directional line items, there is no single number to alarm anyone. A 30% transfer bill looks like three hundred small charges, not one big one. Cost reviews that scan for the biggest line items walk right past it. This is the same reason data transfer and NAT processing are routinely the two largest sources of recoverable waste that teams have never deliberately touched.
The second mechanism is that the decisions that drive transfer are made for resilience, latency, or developer convenience — all good reasons — with the cost as an invisible side effect. Spreading a workload across Availability Zones is correct for fault tolerance; it also generates cross-AZ charges on every east-west call. Going multi-region is correct for latency or data-residency compliance; it also creates standing replication traffic. Putting everything behind a NAT Gateway is the path of least resistance for private subnets; it also taxes every gigabyte to S3 that should have been free.
The third mechanism is microservice chattiness. A monolith making an in-process function call pays nothing. Decompose it into twenty services that call each other over the network — and schedule them across AZs for resilience — and every one of those calls is now potentially a billable cross-AZ round trip. The architecture is more scalable and more resilient; it is also, byte for byte, more expensive to operate, and the cost scales with how chatty the mesh is rather than with how much useful work it does.
The fourth mechanism is growth itself. Transfer scales with users, data volume, and traffic — the very things a healthy company wants more of. A model that was a rounding error at 1,000 users becomes a line worth optimizing at 100,000, but because it grew smoothly rather than spiking, no single moment ever forced a review. By the time someone looks, it has been compounding for a year.
Every silent balloon has the same shape: a decision made for resilience, latency, or convenience generates per-GB traffic; that traffic fragments across the bill so no one number looks alarming; and it scales with growth so it never spikes. The fix is never to stop being resilient — it is to make the traffic visible, then remove only the charges that buy you nothing.
You cannot fix what you cannot see, and the AWS Cost Explorer dashboard hides most of the transfer detail behind a single "Data Transfer" usage-type group. The real resolution lives in the Cost and Usage Report (CUR) — the line-item-level export that names every directional charge.
Start in Cost Explorer for the shape: filter to your account, group by Usage Type, and search the usage-type list for the substrings that flag transfer. The grammar of these usage types is consistent once you learn it, and learning it is most of the battle:
For real attribution — which service, which resource, which workload — you need the CUR, not Cost Explorer. Enable a CUR export to S3 (or use the newer Data Exports), then query it with Athena. The CUR carries the columns that matter: line_item_usage_type (the transfer grammar above), line_item_resource_id (the actual instance, gateway, or bucket), product_region, and line_item_usage_amount (gigabytes). Group transfer rows by resource ID and you can finally answer the question Cost Explorer cannot: which specific component is generating this charge? That single query — sum of transfer cost grouped by resource — is the highest-leverage thing you can run, because it converts a smear across the bill into a ranked list of culprits you can act on one by one.
Almost every transfer fix is configuration, not re-architecture, and several are literally free to turn on. Pull them in this order — the early ones cost nothing and often remove an entire charge category, the later ones trade more effort for the remaining percentage points.
A Gateway VPC endpoint for S3 or DynamoDB routes that traffic directly over the AWS network instead of through your NAT Gateway. The endpoint itself is free — no hourly fee, no per-GB charge — and it eliminates the $0.045/GB NAT processing charge for every byte to S3 or DynamoDB. For any account with private-subnet instances touching S3 heavily, this is the single highest-return change available, and it takes minutes to apply.
How to know you need it: a large "NatGateway-Bytes" line in the CUR sitting next to heavy S3 usage. That combination is the signature of S3 traffic needlessly paying the NAT tax.
Cross-AZ traffic is $0.01/GB each direction. For the chattiest east-west pairs — an application tier and its primary database, two services that call each other constantly — pinning them to the same Availability Zone removes the charge entirely for that traffic. You keep multi-AZ resilience where it matters (across the fleet as a whole, for failover) while removing the cost on the hot paths.
The discipline is selective placement: identify the heaviest cross-AZ pairs from the "Regional-Bytes" CUR rows, co-locate those, and leave genuinely independent or failover-critical components spread. This is a tuning decision, not an all-or-nothing one.
CloudFront helps twice. First, data transfer from S3 or EC2 into CloudFront (origin fetches) is free — so serving through CloudFront removes the origin-to-edge egress charge. Second, CloudFront's own egress to users is cheaper than direct EC2/S3 egress (starting around $0.085/GB and tiering down steeply at volume, plus committed-use discounts), and it caches, so origin fetches shrink dramatically.
For anything serving media, downloads, large API responses, or static assets directly from EC2 or an ALB, moving delivery to CloudFront is usually the biggest single cut to the internet-egress line — and it improves latency at the same time.
Where services must communicate privately and securely, AWS PrivateLink (Interface endpoints) keeps traffic on the AWS backbone and avoids routing through NAT or the public internet. It is not free (hourly + per-GB), but it is typically cheaper and more secure than the NAT path for steady service-to-service traffic, and it removes exposure of that traffic to the internet entirely.
More broadly: keep services that talk to each other in the same region. Inter-region traffic at ~$0.02/GB (or far more from some regions) is justified only when latency or compliance genuinely requires the second region. If a workload went multi-region for reasons that no longer hold, consolidating regions removes the replication tax.
Transfer is billed by the gigabyte, so fewer gigabytes is a linear saving across every category at once. Enable gzip/Brotli compression on API responses and web content, compress objects before cross-region replication, use efficient binary serialization (Protobuf, Avro) instead of verbose JSON on high-volume internal calls, and store/transfer analytics data in columnar compressed formats (Parquet) rather than raw CSV/JSON. A pipeline that compresses 5:1 before moving data cuts the transfer cost of that data by roughly 80% with no architectural change.
When the per-call cross-AZ traffic of a microservice mesh is genuinely large, the structural fix is to reduce the chattiness: batch calls instead of making many small ones, cache aggressively to avoid repeat fetches, co-locate tightly-coupled services, and reconsider whether two services that talk constantly should have been separated at all. This is real engineering effort with slow payback, which is exactly why it ranks last — exhaust the free configuration wins first.
In early 2024, under pressure from regulators (notably the EU Data Act) and from customer complaints about lock-in, AWS — followed by the other major clouds — announced free data transfer out to the internet for customers who are leaving AWS entirely. It is a genuine and welcome change. It is also routinely misread.
What it actually does: if you decide to migrate off AWS and close your account, you can request a credit covering the egress charges for transferring your data out to the internet. It removes the cliff where leaving the cloud was itself made expensive by the very egress fees that lock-in critics had targeted. For an organization genuinely exiting, that is a real reduction in switching cost, and it is the direct answer to the EU Data Act's requirement that providers not charge punitive fees for switching.
What it does NOT do — and this is the misread — is change any of the day-to-day economics covered in this guide. The waiver applies only to the one-time bulk transfer out when you close the account, and it must be requested. It does nothing for your ordinary running bill: serving traffic to your users, cross-AZ chatter, inter-region replication, NAT processing, and normal internet egress are all still billed at the standard per-gigabyte rates. The expensive line you see every month is unaffected.
There are conditions, too. The free-exit egress is for customers actually leaving, not a general discount; it typically requires you to be transferring out and closing the account, and you must apply for the credit rather than receive it automatically. It is best understood as removing a lock-in penalty at the door, not as making egress cheap. The optimization work — endpoints, AZ placement, CloudFront, compression — remains exactly as relevant as before, because it targets the recurring bill, which the waiver never touches.
Free-egress-when-leaving is a real reduction in switching cost and a direct response to the EU Data Act — but it is a one-time, request-based waiver for accounts that are closing. It changes nothing about your monthly transfer bill. Every fix in this guide still applies in full.
| Fix | Cost to implement | Effort | Charge it removes | Typical impact |
|---|---|---|---|---|
| S3 / DynamoDB Gateway endpoint | Free | Minutes | NAT processing on S3/DDB traffic ($0.045/GB) | High — often the #1 win |
| Co-locate chatty services in one AZ | Free | Low–medium | Cross-AZ ($0.01/GB each way) | High on chatty meshes |
| CloudFront for egress | Pay-as-you-go (cheaper) | Low–medium | Origin→edge egress (free) + lower user egress | High for media/downloads |
| PrivateLink (Interface endpoints) | ~$0.01/hr + ~$0.01/GB | Medium | NAT path for private service traffic | Medium + security gain |
| Compression on the wire | Free (CPU only) | Low | Every per-GB charge, proportionally | Linear with ratio (often 50–80%) |
| Consolidate regions | Free | Medium–high | Inter-region replication ($0.02/GB+) | High if multi-region was premature |
| Re-architect chatty patterns | Engineering time | High | Cross-AZ + intra-region volume | Variable, slow payback |
Turn the theory into a repeatable pass. This is the sequence a cost engineer runs on an unfamiliar account to find and kill transfer waste, ordered so the cheap, high-yield steps come first.
Step 1 — Size the problem (30 minutes). In Cost Explorer, filter to the account, group by Usage Type, and sum every row containing "Bytes", "DataTransfer", or "NatGateway". That total is your transfer spend and its share of the bill. If it is under a few percent, stop — your levers are elsewhere. If it is 10%+, continue.
Step 2 — Find the categories (30 minutes). Within that total, separate the four big buckets by usage-type grammar: internet "-Out-Bytes" (egress), "Regional-Bytes" (cross-AZ), region-pair prefixes (inter-region), and "NatGateway-Bytes" (NAT). Now you know which charge dominates rather than treating "data transfer" as one blob.
Step 3 — Attribute to resources (1–2 hours). Query the CUR in Athena: sum line_item_usage_amount and cost, grouped by line_item_resource_id and line_item_usage_type, filtered to transfer usage types. This produces a ranked list of the specific gateways, instances, and buckets generating the charges — the culprits, named.
Step 4 — Apply the free fixes (same day). For every private-subnet workload hitting S3/DynamoDB with NAT charges, add a Gateway endpoint. For the heaviest cross-AZ pairs, plan same-AZ co-location. Turn on response compression. These three alone typically remove the bulk of the recoverable spend at zero cost.
Step 5 — Apply the structural fixes (this quarter). Move internet-facing delivery to CloudFront, replace NAT-routed private traffic with PrivateLink where it pays, and re-examine any multi-region setup whose justification has lapsed. Compress and re-format high-volume analytics data.
Step 6 — Make it stick (ongoing). Set a Cost Anomaly Detection monitor on data-transfer usage types and a budget alert, so the next silent balloon trips an alarm at hundreds of dollars instead of being discovered at tens of thousands a year later. Re-run Step 1 quarterly — transfer scales with growth, so the audit is never truly "done."
To make the structure concrete, here is what moving one gigabyte costs depending purely on where it goes and what it passes through — same byte, wildly different price. This is the table to keep in your head when you design data paths.
| Where the gigabyte goes | Usage-type signature | Price (US region) | Billed direction |
|---|---|---|---|
| Internet → AWS (ingress) | "-In-Bytes" | $0.00 | Free, always |
| AWS → internet (egress), first 10 TB | "-Out-Bytes" | ~$0.09/GB | Out only |
| Across AZs, round trip | "DataTransfer-Regional-Bytes" | $0.02/GB ($0.01 each way) | Both directions |
| Across US regions | region-pair "-Out-Bytes" | ~$0.02/GB | Source ("out") |
| Through a NAT Gateway to S3 | "NatGateway-Bytes" | $0.045/GB processing (+egress if internet) | On processing |
| Same AZ, same region | (generally none) | $0.00 for most same-AZ traffic | — |
Situation: Founder noticed the AWS bill climbing faster than usage and could not find why — no single line item looked large. CloudRoute-routed partner pulled the CUR and found data transfer was 28% of spend: a fat "NatGateway-Bytes" line (the ingestion service in private subnets was writing to S3 through the NAT Gateway), a large "Regional-Bytes" line (EKS scheduled chatty services across AZs), and a growing inter-region line from an analytics replica in a second region added "for latency" months earlier but never actually queried from there.
What CloudRoute did: Three fixes in the first week, all configuration: added an S3 Gateway endpoint (killed the NAT processing on ingestion traffic entirely, at zero cost), pinned the two chattiest EKS services and their primary datastore to the same AZ via topology constraints, and enabled gzip on the API tier. The unused second-region replica was decommissioned in week two. CloudFront was put in front of the dashboard's asset delivery for the long-term egress line.
Outcome: Transfer dropped from 28% to ~9% of the bill within two weeks — roughly a $3,000/month reduction on a $14K bill, with no architecture rewrite and no loss of resilience. The partner engagement was delivered under AWS Well-Architected / optimization funding, so the customer paid $0. CloudRoute's commission was paid by the partner from that AWS funding.
transfer share: 28% → 9% · monthly saving: ~$3,000 · engineering time: ~6 hours · cost to customer: $0
CloudRoute routes you to a vetted AWS partner who pulls your Cost and Usage Report, maps every transfer charge to the resource generating it, and applies the fixes. Often delivered under AWS Well-Architected / optimization funding, so the customer pays $0.