How to allowlist AWS IP ranges
"Allow AWS service X in region Y through the firewall" sounds like a list-copying job. Done well, it is three decisions: use a list AWS maintains for you if one exists, pick exactly the right service code if it doesn't, and automate updates, because AWS changes its ranges frequently and without a fixed schedule.
An IP allowlist for a shared AWS service admits every AWS customer using that service, not just you. Allowing EC2 or AMAZON ranges lets in anything anyone runs on AWS. Treat IP rules as one layer, next to authentication.
Prefer AWS-managed prefix lists
If the rule lives in AWS (a security group, a route table), use an AWS-managed prefix list. AWS keeps it current, and you reference it by ID instead of pasting CIDRs. At the time of writing AWS offers them for:
| Service | Prefix list name |
|---|---|
| CloudFront (origin-facing servers) | com.amazonaws.global.cloudfront.origin-facing, com.amazonaws.global.ipv6.cloudfront.origin-facing |
| DynamoDB | com.amazonaws.region.dynamodb |
| EC2 Instance Connect | com.amazonaws.region.ec2-instance-connect, com.amazonaws.region.ipv6.ec2-instance-connect |
| Ground Station | com.amazonaws.global.groundstation |
| Route 53 health checks | com.amazonaws.region.route53-healthchecks, com.amazonaws.region.ipv6.route53-healthchecks |
| S3 | com.amazonaws.region.s3 |
| S3 Express One Zone | com.amazonaws.region.s3express |
| Secrets Manager (managed external secrets) | com.amazonaws.region.secretsmanager-managed-external-secrets |
| VPC Lattice | com.amazonaws.region.vpc-lattice, com.amazonaws.region.ipv6.vpc-lattice |
Check AWS's current table before you build: it also gives each list's weight, the number of rules it counts as against your security group quota. The CloudFront list is heavy enough to crowd out other rules.
Prefix list IDs differ between Regions, so look them up by name:
PL_ID=$(aws ec2 describe-managed-prefix-lists --region eu-west-2 \
--filters Name=prefix-list-name,Values=com.amazonaws.global.cloudfront.origin-facing \
--query 'PrefixLists[0].PrefixListId' --output text)
# See what's in it
aws ec2 get-managed-prefix-list-entries --region eu-west-2 --prefix-list-id "$PL_ID"
# Allow HTTPS from it
aws ec2 authorize-security-group-ingress --region eu-west-2 \
--group-id sg-0123456789abcdef0 \
--ip-permissions "IpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds=[{PrefixListId=$PL_ID}]"
In Terraform:
data "aws_ec2_managed_prefix_list" "cloudfront" {
name = "com.amazonaws.global.cloudfront.origin-facing"
}
resource "aws_vpc_security_group_ingress_rule" "https_from_cloudfront" {
security_group_id = aws_security_group.origin.id
prefix_list_id = data.aws_ec2_managed_prefix_list.cloudfront.id
ip_protocol = "tcp"
from_port = 443
to_port = 443
}
For AWS-to-AWS traffic, you may not need IP rules at all: gateway VPC endpoints for S3 and DynamoDB keep that traffic inside AWS's network.
Pick the right service code
Firewalls outside AWS need raw CIDRs from ip-ranges.json. Most mistakes start with the wrong code:
| You want to… | Use | Not |
|---|---|---|
| Let CloudFront reach your origin | CLOUDFRONT_ORIGIN_FACING | CLOUDFRONT, which also covers the edge addresses viewers connect to |
| Let Route 53 health checks in | ROUTE53_HEALTHCHECKS | ROUTE53_HEALTHCHECKS_PUBLISHING, which Route 53 uses only internally |
| Allow EC2 Instance Connect from the console | EC2_INSTANCE_CONNECT for your instance's region | All of EC2 |
| Accept calls from API Gateway integrations | API_GATEWAY (outbound-only addresses) | Those ranges as a destination: API endpoints aren't hosted on them |
| Let servers reach S3 in one Region | S3 for that Region; from inside a VPC, a gateway endpoint is simpler | AMAZON |
| Query Route 53 name servers | ROUTE53 | Health check ranges |
Filter ip-ranges.json with jq
curl -sO https://ip-ranges.amazonaws.com/ip-ranges.json
# IPv4 and IPv6 ranges for one service in one Region
jq -r '(.prefixes[] | select(.service=="S3" and .region=="eu-west-1") | .ip_prefix),
(.ipv6_prefixes[] | select(.service=="S3" and .region=="eu-west-1") | .ipv6_prefix)' ip-ranges.json
# EC2 ranges that aren't used by S3 (a range tagged S3 and EC2 belongs to S3)
jq -r '[.prefixes[] | select(.service=="S3") | .ip_prefix] as $s3
| .prefixes[] | select(.service=="EC2" and .region=="us-east-1") | .ip_prefix
| select(IN($s3[]) | not)' ip-ranges.json
# Load a list into an ipset
ipset create aws-s3-euw1 hash:net family inet
jq -r '.prefixes[] | select(.service=="S3" and .region=="eu-west-1") | .ip_prefix' ip-ranges.json \
| xargs -n1 ipset add aws-s3-euw1
Filtering on .region includes the Region's Local Zones and Wavelength Zones. To get only the Region itself, filter on .network_border_group instead. To merge adjacent ranges into fewer rules, Python's standard library can collapse them:
jq -r '.prefixes[] | select(.service=="S3" and .region=="eu-west-1") | .ip_prefix' ip-ranges.json \
| python3 -c 'import ipaddress, sys; [print(n) for n in ipaddress.collapse_addresses(ipaddress.ip_network(l.strip()) for l in sys.stdin)]'
Build a list in your browser
This builder does the same filtering on the current ip-ranges.json. It downloads the file from AWS only when you click, and it can output plain CIDRs, nginx set_real_ip_from lines or a JSON array.
Keep lists current with SNS
AWS announces every change to ip-ranges.json on an Amazon SNS topic in us-east-1:
arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged
Subscribe an SQS queue, a Lambda function or an email address:
aws sns subscribe --region us-east-1 \
--topic-arn arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged \
--protocol sqs --notification-endpoint arn:aws:sqs:us-east-1:123456789012:ip-ranges-changed
For SQS, the queue also needs an access policy that allows sns.amazonaws.com to send messages (sqs:SendMessage) when aws:SourceArn is arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged. Without it, the subscription is created but notifications never arrive.
Each message carries create-time, synctoken, md5 and url. An update job should:
- download the file and check its
syncTokenand MD5 (md5sum ip-ranges.json) against the message; a mismatch usually means a newer version is already out; - ignore messages older than the version you already applied, because notifications can arrive out of order;
- filter for your service and Region, and compare with the current list. Most updates don't touch the ranges you care about;
- refuse to apply an empty or suspiciously short list, and keep the last good one;
- also run on a schedule, because AWS notes that delivery depends on the endpoint being available.
Services that don't publish ranges
AWS doesn't publish ranges for every service. The common gaps:
- Lambda. There are no Lambda ranges; outbound traffic uses shared AWS address space that changes. If a partner must allowlist your function, attach it to private subnets in a VPC and route internet traffic through a NAT gateway with an Elastic IP address. The partner then allows one address that belongs to you. The same pattern works for containers and anything else in a VPC.
- QuickSight. Not in
ip-ranges.json. The ranges it connects to your databases from are listed per Region in its own documentation, now published as the Amazon Quick user guide. - Services only in prefix lists, such as Ground Station, S3 Express One Zone, VPC Lattice and Secrets Manager's managed external secrets. Reading their entries needs an AWS account.
- Amazon SES. Its sending servers are mostly absent from the file. Authenticate SES mail with SPF and DKIM, not IP allowlists.
Common mistakes
- Forgetting IPv6: many services publish both families.
- Copying a list once and never updating it.
- Allowlisting
CLOUDFRONT_ORIGIN_FACINGand stopping there: every CloudFront distribution uses those addresses, including other people's. Add a secret header check or use VPC origins, as the CloudFront guide explains.
Sources
- AWS: AWS-managed prefix lists
- AWS: Reference prefix lists in your AWS resources
- AWS: Find the IP address ranges for AWS services
- AWS: AWS IP address ranges notifications
- AWS: Syntax for AWS IP address range JSON (API Gateway egress-only, range overlaps)
- AWS: IP address ranges of Amazon Route 53 servers
- AWS: Enable internet access for VPC-connected Lambda functions