The AWS ip-ranges.json format, explained
AWS publishes the public IP ranges it uses in a single JSON file, https://ip-ranges.amazonaws.com/ip-ranges.json. Each entry pairs an address range with a service code, a region and a network border group. This page explains every field, why the same address matches several entries, which entry to believe, and how and when the file changes.
This site reads the same file: your browser downloads it straight from AWS whenever you run a lookup. How it works has the details.
What the file looks like
An excerpt from the version published on 2026-09-24. The real file is a few megabytes, and the arrays hold thousands of entries.
{
"syncToken": "1790232426",
"createDate": "2026-09-24-06-47-06",
"prefixes": [
{
"ip_prefix": "3.5.140.0/22",
"region": "ap-northeast-2",
"service": "AMAZON",
"network_border_group": "ap-northeast-2"
},
...
],
"ipv6_prefixes": [
{
"ipv6_prefix": "2600:1f18::/33",
"region": "us-east-1",
"service": "EC2",
"network_border_group": "us-east-1"
},
...
]
}
Fields
| Field | Type | Meaning |
|---|---|---|
syncToken |
string | The publication time as Unix epoch seconds, for example "1790232426" (2026-09-24 06:47:06 UTC). It is different in every version, so it is the value to use when you check whether the file has changed. |
createDate |
string | The same moment, in UTC, formatted YYYY-MM-DD-HH-MM-SS, for example "2026-09-24-06-47-06". It is not ISO 8601 and has no time zone marker; it is always UTC. AWS calls it the publication time, but the file usually reaches the download URL some time later (see updates). |
prefixes |
array | The IPv4 entries. |
ipv6_prefixes |
array | The IPv6 entries. Same shape, except that the range is in ipv6_prefix. |
ip_prefix |
string | An IPv4 range in CIDR notation, such as 3.5.140.0/22. AWS notes that it may announce a range to the internet as several smaller routes, so the routing table won't always show the same prefix. |
ipv6_prefix |
string | An IPv6 range in CIDR notation, such as 2600:1f18::/33. Compare parsed addresses rather than strings: the same address can be written in more than one way. |
region |
string | The AWS Region code, such as us-east-1, or GLOBAL for ranges whose traffic can come from, or go to, many Regions (see regions and GLOBAL). |
service |
string | A service code such as AMAZON, EC2, S3 or CLOUDFRONT. The service codes reference explains each one, including which codes mark traffic from AWS rather than services you connect to. |
network_border_group |
string | The set of Availability Zones, Local Zones or Wavelength Zones from which AWS advertises the range, or GLOBAL. For an ordinary regional range it equals the region code. Local Zones look like us-west-2-lax-1 (Los Angeles) and Wavelength Zones like us-east-1-wl1-bos-wlz-1 (AWS's Boston Wavelength Zone). |
Writing a parser that keeps working
- Don't whitelist codes. New service codes, region codes and border groups appear without notice, and AWS's documented list of valid values sometimes lags behind the file itself. Treat an unknown value as unknown, not as an error.
- Ignore keys you don't recognise. The format has gained fields before (the border group was added in 2020).
- Expect one entry per range and service. A range used by three services appears three times, once per service code (see shared ranges).
- Don't rely on order. Entries are not sorted in any documented way.
- Validate the shape. A captive Wi-Fi portal or proxy can hand you an HTML page instead. Check that
syncToken,createDateand both arrays are present before replacing a copy you already have.
Shared ranges: one prefix, several services
The file has one entry per range and service, so a range used by several services is listed several times with the same region and border group:
jq -c '.prefixes[] | select(.ip_prefix == "3.5.140.0/22")' ip-ranges.json
{"ip_prefix":"3.5.140.0/22","region":"ap-northeast-2","service":"AMAZON","network_border_group":"ap-northeast-2"}
{"ip_prefix":"3.5.140.0/22","region":"ap-northeast-2","service":"S3","network_border_group":"ap-northeast-2"}
{"ip_prefix":"3.5.140.0/22","region":"ap-northeast-2","service":"EC2","network_border_group":"ap-northeast-2"}
That doesn't mean three services share the addresses. AWS's range overlap rules say that when one service runs on another's resources, the range is listed under both codes but used only by the first. Their example is exactly this case: a range tagged both S3 and EC2 is used exclusively by Amazon S3. To find ranges used by EC2 itself, take the EC2 ranges that no other service code claims.
AMAZON is different again. It is the catch-all: every address in the file is covered by an AMAZON entry, either on the same prefix or on a larger one around it. A range listed only as AMAZON means AWS uses it but doesn't say for what.
Nesting: ranges inside ranges
Besides sharing prefixes, entries nest. A small range for a specific service often sits inside a larger EC2 or AMAZON block, and the small range may have no AMAZON entry of its own. For 3.0.5.33 the file has:
3.0.5.32/29 EC2_INSTANCE_CONNECT ap-southeast-1
3.0.0.0/15 AMAZON ap-southeast-1
3.0.0.0/15 EC2 ap-southeast-1
The /29 is the part of the EC2 block that EC2 Instance Connect uses. Nesting also runs the other way: some small ranges tagged only AMAZON sit inside large EC2 blocks, carved out for something AWS doesn't name. And a regional range can sit inside a GLOBAL one (see below).
Choosing the most specific range
Because of sharing and nesting, an address usually matches several entries. This site picks one headline answer with the following rule, and always shows the other matches underneath:
- Collect every entry whose range contains the address.
- The most specific range wins. That is the one with the longest prefix (a /29 beats a /15). Larger ranges around it are context, not the answer.
- Within that range, a named service beats
EC2, andEC2beatsAMAZON. This follows AWS's documented overlap rule, applied to the most specific range only. - If only
EC2is left, say "EC2 address space", not "an EC2 instance". Load balancers, NAT gateways, Lambda and many other services use EC2 addresses too. - If the most specific range is tagged only
AMAZON, the service isn't disclosed. When a largerEC2range encloses it, it is a carve-out: a range AWS has set aside inside EC2 space for something else. Reporting it as EC2 would be misleading. - Take the region and border group from the most specific range. If an enclosing range is
GLOBAL, mention that as well.
Worked examples from the file published on 2026-09-24 (ranges change, so a live lookup may differ):
| Address | Matching ranges, most specific first | Headline |
|---|---|---|
| 3.18.1.10 | 3.16.0.0/14 AMAZON, EC2 · us-east-2 |
EC2 address space, US East (Ohio) |
| 3.5.140.1 | 3.5.140.0/22 AMAZON, S3, EC2 · ap-northeast-2 |
Amazon S3 (the EC2 tag is dropped) |
| 3.0.5.33 | 3.0.5.32/29 EC2_INSTANCE_CONNECT3.0.0.0/15 AMAZON, EC2 · ap-southeast-1 |
EC2 Instance Connect |
| 44.248.100.1 | 44.248.100.0/23 AMAZON44.224.0.0/11 AMAZON, EC2 · us-west-2 |
Undisclosed service, carved out of EC2 space |
| 64.252.81.1 | 64.252.81.0/24 AMAZON, EC2 · sa-east-164.252.64.0/18 AMAZON, CLOUDFRONT, CLOUDFRONT_ORIGIN_FACING · GLOBAL |
EC2 address space in sa-east-1, inside a global CloudFront block |
Two tempting shortcuts give wrong answers. Taking the first match depends on file order, which means nothing. Merging every tag that covers the address turns 44.248.100.1 into "EC2" and 3.0.5.33 into "EC2 and EC2 Instance Connect", hiding exactly the detail the small range was published to give.
Whatever the headline, a match identifies AWS's use of the range. It never identifies the AWS customer using an address.
Regions, GLOBAL and partitions
region is where AWS advertises the range, which isn't necessarily where anyone using it is. Some points that trip up parsers and people:
GLOBALmeans traffic for the range can arrive at, or come from, many Regions (in AWS's words, up to all of them). CloudFront edge servers and Global Accelerator's static addresses are the typical cases. But it doesn't always mean an edge network: someEC2andS3ranges are themselvesGLOBAL, and some regional ranges sit insideGLOBALparents, like the sa-east-1 example above. Show both values when they differ.- Local Zones can be far from their parent Region, sometimes in another country:
us-east-1-bue-1is in Buenos Aires. Use the border group, not the region, to describe the location, and see the geolocation feed for cities. - One file covers every partition. There is no partition field; the region code tells you.
cn-Regions belong to AWS China (aws-cn),us-gov-Regions to AWS GovCloud (US) (aws-us-gov),eusc-Regions to the AWS European Sovereign Cloud (aws-eusc), and everything else to the commercialawspartition. Partitions are separate clouds with separate accounts, so an address in a China Region has nothing to do with a commercial AWS account. - Region codes can appear before they are documented. A code without a published name is still a real region code; show it as-is.
How and when the file changes
- No fixed schedule. AWS publishes a new version whenever its ranges change, which at the time of writing (September 2026) usually happened several times a day, at irregular intervals.
- Most versions change very little. Often one or two ranges differ, and the same range can be added and removed repeatedly. Real drift (new ranges, retired ranges, a range moving to another region) accumulates over weeks.
- There is a publication lag.
createDateis when AWS produced the version, not when you could first download it. In our measurements the file reached the download URL well over an hour after itscreateDate. The 2026 research article has the measured figures. - Identify versions by
syncToken, or by the file's MD5 hash, which the ETag header carries. Not by the HTTPLast-Modifieddate or the time you downloaded it. - AWS keeps no archive. Only the current version is served. If you need to know what the file said last month, save copies yourself; third parties such as seligman/aws-ip-ranges keep public histories.
The file is served uncompressed, and its CORS header allows any website to read it. The server also honours Range requests, so you can read the first few bytes to check the syncToken without downloading everything:
curl -s -r 0-63 https://ip-ranges.amazonaws.com/ip-ranges.json
Change notifications (SNS)
AWS announces every change on the Amazon SNS topic
arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged
You can subscribe from any AWS account, in the us-east-1 Region, for example by email:
aws sns subscribe --region us-east-1 \
--topic-arn arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged \
--protocol email --notification-endpoint you@example.com
Each message carries create-time, synctoken, md5 (the hash of the new file) and url. AWS warns that notifications can arrive out of order, so compare synctoken values rather than trusting arrival order. Delivery also depends on your endpoint being available, and AWS suggests checking the file periodically as well. Anything that depends on current ranges should do both.
jq recipes
Download the file once, then query it locally:
curl -sO https://ip-ranges.amazonaws.com/ip-ranges.json
Version and publication time
jq -r '.syncToken, .createDate' ip-ranges.json
# createDate as ISO 8601
jq -r '.createDate | strptime("%Y-%m-%d-%H-%M-%S") | todate' ip-ranges.json
2026-09-24T06:47:06Z
Ranges for one service in one region
jq -r '.prefixes[] | select(.service == "S3" and .region == "eu-west-1") | .ip_prefix' ip-ranges.json
jq -r '.ipv6_prefixes[] | select(.service == "S3" and .region == "eu-west-1") | .ipv6_prefix' ip-ranges.json
EC2 ranges not claimed by another service
jq -r '.prefixes | group_by(.ip_prefix)[]
| select(map(.service) | index("EC2") != null and all(. == "EC2" or . == "AMAZON"))
| .[0].ip_prefix' ip-ranges.json
This applies AWS's overlap rule per prefix. It doesn't remove nested ranges: an EC2 block can still contain smaller ranges that belong to other services.
Prefixes listed under more than one service
jq -r '.prefixes | group_by(.ip_prefix)[] | select(length > 1)
| [.[0].ip_prefix, (map(.service) | join(",")), .[0].region] | @tsv' ip-ranges.json
Border groups of a region
jq -r '[.prefixes[], .ipv6_prefixes[] | select(.region == "us-west-2") | .network_border_group] | unique[]' ip-ranges.json
The result lists the region itself plus its Local Zones and Wavelength Zones.
Every range containing an IPv4 address, most specific first
jq -r --arg ip 44.248.100.1 '
def ip2n: split(".") | map(tonumber) | .[0]*16777216 + .[1]*65536 + .[2]*256 + .[3];
($ip | ip2n) as $n
| [ .prefixes[]
| (.ip_prefix | split("/")) as [$net, $len]
| select(($net | ip2n) as $s | $n >= $s and $n < $s + pow(2; 32 - ($len | tonumber)))
| . + {len: ($len | tonumber)} ]
| group_by(.len) | reverse[]
| [.[0].ip_prefix, (map(.service) | join(",")), .[0].region] | @tsv' ip-ranges.json
44.248.100.0/23 AMAZON us-west-2
44.224.0.0/11 AMAZON,EC2 us-west-2
Output from the 2026-09-24 file. jq has no IPv6 arithmetic; for IPv6, use Python's ipaddress module as shown in how to check if an IP belongs to AWS, or paste the address into the lookup.
Other AWS IP lists
The CloudFront list
AWS serves CloudFront's IPv4 ranges on their own at d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips, as two arrays: CLOUDFRONT_GLOBAL_IP_LIST and CLOUDFRONT_REGIONAL_EDGE_IP_LIST. When we compared them, they matched the CLOUDFRONT entries in ip-ranges.json exactly: the global list is the GLOBAL entries and the regional edge list is the rest. It has no IPv6 ranges and no origin-facing section. Every IPv4 CLOUDFRONT_ORIGIN_FACING range sits inside one of its ranges, so an origin firewall built from it would work, but it would also admit all of CloudFront's edge space. To lock an origin to CloudFront, use the CLOUDFRONT_ORIGIN_FACING entries or the managed prefix list; the CloudFront guide covers that.
AWS-managed prefix lists
Inside AWS, security groups and route tables can reference AWS-managed prefix lists instead of raw CIDRs, and AWS keeps them current. Examples are com.amazonaws.global.cloudfront.origin-facing, com.amazonaws.region.s3 and com.amazonaws.region.dynamodb. A few services, such as AWS Ground Station, Amazon S3 Express One Zone and Amazon VPC Lattice, publish their ranges only this way. Reading a prefix list needs an AWS account:
aws ec2 describe-managed-prefix-lists --filters Name=owner-id,Values=AWS
aws ec2 get-managed-prefix-list-entries --prefix-list-id pl-0123456789abcdef0
Each list has a weight that counts against security group rule quotas. The CloudFront origin-facing list is heavy, so check AWS's documentation before adding it to a group that already has many rules.
The geolocation feed
ip-ranges.amazonaws.com/geo-ip-feed.csv gives a country, subdivision and city for AWS ranges, in the RFC 8805 geofeed format. AWS publishes it because a Local Zone isn't in the same place as its parent Region. A line looks like this:
1.178.4.0/23,US,US-VA,Ashburn,
Treat the city as the location of AWS's infrastructure, not of whoever uses an address.
Amazon SES sending ranges
Amazon SES mail servers are mostly absent from ip-ranges.json. The SES sending ranges are published in the SPF record of amazonses.com:
dig +short TXT amazonses.com | grep spf1
Related
- Service codes in ip-ranges.json, including
AMAZONandEC2 - Allowlist AWS IP ranges
- Find the AWS region of an IP address
- What AWS's IP data can and can't tell you (2026)
Sources
- AWS: Syntax for AWS IP address range JSON (fields and range overlaps)
- AWS: AWS IP address ranges (considerations, BYOIP, geolocation feed)
- AWS: Find the IP address ranges for AWS services (official jq examples)
- AWS: AWS IP address ranges notifications
- AWS: AWS-managed prefix lists
- AWS: Locations and IP address ranges of CloudFront edge servers
- AWS: Partitions