AWS WAF Block Country and CloudFront Geo Restriction: Geo Blocking on CloudFront, ALB, API Gateway and EC2, With Terraform, Pricing and Testing
To block a country on AWS, you have two main tools. CloudFront geographic restrictions (geo restriction) is a free switch on a CloudFront distribution: you add an allow list or a block list of countries, and visitors from anywhere else get HTTP 403 with the message "The Amazon CloudFront distribution is configured to block access from your country". It applies to the whole distribution, country level only. AWS WAF geo match (the answer to "aws waf block country") is a rule in a WAF web ACL that matches the country, and even the state or province, of each request. You can combine it with paths, IP exceptions, rate limits and custom error pages, and it works on CloudFront, Application Load Balancers, API Gateway REST APIs, AppSync, Cognito and more. WAF costs $5 a month per web ACL, $1 per rule and $0.60 per million requests. For NLBs and EC2 traffic that is not HTTP, use AWS Network Firewall geoip rules. Route 53 geolocation routing sends users to different servers by location but does not block anyone. Below: every option compared, step-by-step setup in the console, CLI, Terraform and CloudFormation, the country code traps, testing, accuracy and VPNs, pricing, and what the error means if you are the one being blocked.
Two things happened to Ethan in the same week. First, a client that sells online cooking courses signed a licensing deal with a celebrity chef: the new videos could be sold only in the United States, Canada and the United Kingdom, and the contract asked for "reasonable technical measures" to keep everyone else out. Second, Jake, on holiday abroad, tried to download a database installer for a side project and got a plain white page: "403 ERROR. The request could not be satisfied. The Amazon CloudFront distribution is configured to block access from your country." He sent Ethan a screenshot with one line: "Did someone block an entire country? Can you do that?" Yes, Ethan replied, and he was about to do it himself.
New to CloudFront or WAF? The cards below explain both services, the CloudFront 403 error page, CloudFront pricing and Route 53.
What geo blocking is, and what it is not
Geo blocking (also written geoblocking, and called geographic restriction or geo restriction) means refusing requests based on where the visitor appears to be. Every request on the internet arrives from an IP address, and commercial databases map IP addresses to countries, and often to regions and cities. A geo block looks up the visitor's IP address in such a database and then either serves the request or refuses it.
Companies geo block for a handful of reasons:
- Licensing and rights. Video, music, sports, courses and software are often licensed country by country, like Ethan's cooking videos.
- Legal and compliance. Trade sanctions, gambling laws, data protection rules, or a product that is not approved in some markets.
- Business scope. A shop that ships only within one country, or a local service that has no customers abroad.
- Reducing attack traffic. If all your real users are in three countries, blocking the rest removes a large share of bots, scrapers and login attacks before they reach your application.
Geo blocking is a useful filter, but it is not an identity check. It sees the IP address, not the person. Someone on a VPN, a corporate network or a mobile carrier that routes through another country can appear to be somewhere they are not, and a small share of addresses are simply mapped to the wrong country. Treat geo blocking as one layer that keeps most of the wrong traffic out, and pair it with real authentication for anything that matters. The accuracy section covers this in detail.
Jake: "So when I got that error on holiday, the website was not broken. It just thought I was in the wrong country?"
Ethan: "You were in the wrong country, as far as that site is concerned. Their distribution only serves certain countries, and your hotel's IP address was outside the list."
Every way to block or allow countries on AWS
AWS gives you four tools that look at location, and they work at different layers. Picking the right one is most of the job.
| Tool | Works on | Granularity | Extra cost | Best for |
|---|---|---|---|---|
| CloudFront geo restriction | One CloudFront distribution | Country; whole distribution | None | Licensing a whole site or app to a list of countries |
| AWS WAF geo match | CloudFront, ALB, API Gateway REST APIs, AppSync, Cognito user pools, App Runner, Verified Access, Amplify | Country or region (state, province); any path; combined with other rules | $5/web ACL + $1/rule a month + $0.60 per million requests | Most geo blocking needs, especially "block country" with exceptions |
| AWS Network Firewall geoip | All traffic routed through the firewall in your VPC, any protocol | Country; source or destination | $0.395 per endpoint-hour + $0.065 per GB (US East) | NLBs, EC2, non-HTTP protocols, outbound traffic |
| Route 53 geolocation routing | DNS answers | Continent, country, US state | Standard DNS query prices | Sending users to the nearest or local version; not a block |
Two things are missing from that list on purpose. Security groups and network ACLs filter by IP address and port, not by country; you could paste a country's IP ranges into them, but rule limits and constantly changing address blocks make it impractical. And IAM policies have no country condition at all; the closest you get is aws:SourceIp for known address ranges, covered in our aws:SourceIp guide.
The short decision: if everything is behind CloudFront and you just need "only these countries," use CloudFront geo restriction. If you need anything more (one path only, a state or province, a list of IP addresses that should get through anyway, a friendly error page, or a resource that is not CloudFront), use AWS WAF. If the traffic is not HTTP at all, use Network Firewall.
How CloudFront geo restriction works
CloudFront serves your content from hundreds of edge locations around the world. Normally it serves anyone who asks. When you turn on geographic restrictions for a distribution, each edge location checks the viewer's country before serving anything:
- You set the distribution to an allow list (only these countries may access it) or a block list (everyone except these countries may access it).
- A viewer requests a file. DNS sends them to a nearby edge location, which may be in a different country from the viewer.
- The edge location looks up the viewer's IP address (not the edge location's country) in a third-party geolocation database.
- If the viewer's country is not allowed, CloudFront returns 403 Forbidden and never contacts your origin. If it is allowed, the request continues as usual.
The important facts to know before you rely on it:
- It applies to the entire distribution. Every path and every cache behavior gets the same rule. To restrict only
/premium/while leaving the rest open, you need a second distribution or AWS WAF. - It is country level only. No states, provinces or cities.
- The overall accuracy of the IP-to-country mapping is about 99.8%, and it varies by Region.
- If CloudFront cannot determine a viewer's location, it serves the content. Unknown locations are allowed, even on an allow list.
- It costs nothing extra. You pay normal CloudFront request and data transfer charges.
- Certificate validation is exempt. If you use a CloudFront managed certificate, requests to
/.well-known/pki-validation/skip geo restriction so certificate authorities in any country can validate and renew it. - Blocked requests never reach AWS WAF. If you also attach a web ACL, only requests that pass geo restriction are inspected by WAF.
- You can customize the 403 page. A custom error response for status 403 lets you show your own page, and you choose how long CloudFront caches the error (10 seconds by default).
What a blocked visitor sees
Without a custom error page, the visitor gets CloudFront's standard error page:
403 ERROR
The request could not be satisfied.
The Amazon CloudFront distribution is configured to block access from your country.
We can't connect to the server for this app or website at this time. There might be too much
traffic or a configuration error. Try again later, or contact the app or website owner.
Generated by cloudfront (CloudFront)
Request ID: ...
That message is accurate but unfriendly, and it gives a licensed-content customer no idea what to do. Most businesses replace it with a short page that explains the content is not available in the visitor's region, which is covered in the setup section below.
Set up CloudFront geo restriction: console, CLI, Terraform, CloudFormation and CDK
In the console
- Open the CloudFront console and choose Distributions, then the distribution you want to restrict.
- Open the Security tab and find Geographic restrictions.
- Choose Edit.
- Select Allow list to name the only countries that may access the distribution, or Block list to name the countries that may not.
- Add the countries and choose Save changes.
- Wait for the distribution's status to change from Deploying to the date of the last change. This usually takes a few minutes.
For Ethan's licensing case, the answer was an allow list with the United States, Canada and the United Kingdom. An allow list is safer for licensing because any country you forget is blocked; with a block list, any country you forget is open.
The country code traps
The CLI, the API, Terraform and CloudFormation use ISO 3166-1 alpha-2 two-letter codes, and three of them catch people out:
- The United Kingdom is
GB, notUK.UKis not a valid code and the update fails (or, in some tools, the country is silently missing). - Hong Kong is
HKand is listed separately from China (CN); Macao isMOand Taiwan isTW. - Overseas territories often have their own codes: Puerto Rico is
PR, GuamGU, the US Virgin IslandsVI, GibraltarGI, JerseyJEand GuernseyGG. A "US only" allow list that contains onlyUSblocks Puerto Rico. Check which territories your contract or shipping policy covers.
The API also uses the older values whitelist and blacklist for the restriction type, while the console calls them allow list and block list. They mean the same thing.
With the AWS CLI
CloudFront updates replace the whole distribution configuration, so you fetch it, edit it and send it back with the current ETag:
aws cloudfront get-distribution-config --id E1A2B3C4D5E6F7 --output json > dist.json
Copy the DistributionConfig object from dist.json into a new file, config.json, note the ETag value, and change the Restrictions block:
"Restrictions": {
"GeoRestriction": {
"RestrictionType": "whitelist",
"Quantity": 3,
"Items": ["US", "CA", "GB"]
}
}
aws cloudfront update-distribution --id E1A2B3C4D5E6F7 --if-match E2QWRUHEXAMPLE --distribution-config file://config.json
Quantity must equal the number of items. For a block list use "RestrictionType": "blacklist"; to turn geo restriction off, use "RestrictionType": "none" with "Quantity": 0 and no Items.
With Terraform
The restrictions block is required on every aws_cloudfront_distribution, which is why many Terraform files already contain a restriction_type = "none" you can change:
resource "aws_cloudfront_distribution" "courses" {
# ... origins, default_cache_behavior, viewer_certificate ...
restrictions {
geo_restriction {
restriction_type = "whitelist" # or "blacklist" / "none"
locations = ["US", "CA", "GB"] # ISO 3166-1 alpha-2; "none" needs []
}
}
custom_error_response {
error_code = 403
response_code = 403
response_page_path = "/errors/not-available.html"
error_caching_min_ttl = 60
}
}
With CloudFormation
CoursesDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
# ... Origins, DefaultCacheBehavior, ViewerCertificate ...
Restrictions:
GeoRestriction:
RestrictionType: whitelist
Locations:
- US
- CA
- GB
With the AWS CDK
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
new cloudfront.Distribution(this, 'Courses', {
defaultBehavior: { origin: siteOrigin },
geoRestriction: cloudfront.GeoRestriction.allowlist('US', 'CA', 'GB'),
// or cloudfront.GeoRestriction.denylist('...')
});
A friendly "not available in your region" page
Put a small HTML page in your origin (for example /errors/not-available.html in the S3 bucket), then add a custom error response on the distribution's Error pages tab: HTTP error code 403, customize the response, response page path /errors/not-available.html, response code 403, and an error caching minimum TTL of a minute or so. Keep the response code at 403 (or use 451, "Unavailable For Legal Reasons," if a legal restriction is the cause and your page says so); returning 200 for a blocked page confuses search engines and monitoring.
One trap: custom error responses apply to every 403 the distribution produces, not just geo blocks. Single-page apps often map 403 to /index.html with a 200 so that deep links work. If your distribution does that, blocked visitors get your app shell instead of a clear message, and the app then fails in confusing ways. Test from a blocked location after you add geo restriction to an app with that setting, and consider moving the deep-link handling to a CloudFront Function so 403 can keep its real meaning.
Jake: "So that ugly white page I got was the default one?"
Ethan: "Exactly. Five minutes of work would have shown you a page saying the download is not available in your region and where to get it instead."
Block a country with AWS WAF (geo match)
"AWS WAF block country" is the most searched version of this question, and WAF is the more flexible tool. A geo match statement in a WAF rule matches requests by country of origin; you set the rule's action to Block, Allow, Count, CAPTCHA or Challenge, and you can nest it inside AND, OR and NOT statements with any other condition WAF understands. In the newer WAF console, web ACLs are called protection packs (web ACL); the idea is the same.
How the geo match statement works
- WAF finds the country and region of each request from its IP address, using MaxMind GeoIP databases. By default it uses the address that connected to AWS; you can tell it to use an address from a header such as
X-Forwarded-Forinstead (the forwarded IP configuration), which is only safe if a proxy you trust sets that header. - Country codes are ISO 3166-1 alpha-2 (
US,GB,HK). Region codes are ISO 3166-2 subdivisions such asUS-ORfor Oregon. - Every request the statement inspects gets labels, whether it matches or not:
awswaf:clientip:geo:country:USandawswaf:clientip:geo:region:US-OR(orawswaf:forwardedip:geo:...with a forwarded IP). When the location is unknown, the label usesXX, for exampleawswaf:clientip:geo:country:XX. - To act on regions, you need two rules: a geo match rule (often with the Count action) that adds the labels, and a later label match rule that blocks or allows by region label. Labels are added at the end of a rule's evaluation, so the label match must be in a separate rule with a higher priority number.
- A geo match statement costs 1 WCU (web ACL capacity unit), so it barely touches your capacity.
Allow only some countries (the licensing pattern)
WAF has no "allow list" switch; you build it with a NOT statement. This rule blocks every request that does not come from the US, Canada or the UK:
{
"Name": "allow-only-us-ca-gb",
"Priority": 0,
"Statement": {
"NotStatement": {
"Statement": {
"GeoMatchStatement": { "CountryCodes": ["US", "CA", "GB"] }
}
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "allow-only-us-ca-gb"
}
}
Leave the web ACL's default action at Allow, so everything this rule does not block continues to your other rules and then to your application. Unlike CloudFront geo restriction, requests whose country is unknown do not match US, CA or GB, so this rule blocks them. If you would rather let unknown locations through, add an OR with a label match on awswaf:clientip:geo:country:XX inside the NOT (this needs a geo match rule earlier in the web ACL to add the labels).
Block specific countries (the block list pattern)
For a block list, the geo match statement goes directly in a Block rule:
"Statement": {
"GeoMatchStatement": { "CountryCodes": ["KP", "IR", "SY", "CU"] }
},
"Action": { "Block": {} }
Your legal or compliance team decides which countries belong on the list; the example above is the kind of list companies use for trade sanctions programs, not a recommendation.
Allow countries plus specific IP addresses
"How do I allow only these countries, but also my office, my monitoring service and a partner abroad?" Create a WAF IP set with those addresses, then put both conditions inside the NOT, joined by OR:
"Statement": {
"NotStatement": {
"Statement": {
"OrStatement": {
"Statements": [
{ "GeoMatchStatement": { "CountryCodes": ["US", "CA", "GB"] } },
{ "IPSetReferenceStatement": {
"ARN": "arn:aws:wafv2:us-east-1:123456789012:global/ipset/partners/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
} }
]
}
}
}
},
"Action": { "Block": {} }
Read it as: block the request unless it comes from an allowed country or from an allowed IP address. The same structure answers "how to whitelist countries and IP in CloudFront": CloudFront geo restriction alone cannot add IP exceptions, but a WAF web ACL on the distribution can.
Block a country on one path only
Geo restriction covers a whole distribution; WAF can scope a geo block to part of a site. To keep checkout and account pages to your shipping countries while the blog stays open to everyone:
"Statement": {
"AndStatement": {
"Statements": [
{ "ByteMatchStatement": {
"FieldToMatch": { "UriPath": {} },
"PositionalConstraint": "STARTS_WITH",
"SearchString": "/checkout",
"TextTransformations": [ { "Priority": 0, "Type": "LOWERCASE" } ]
} },
{ "NotStatement": { "Statement": {
"GeoMatchStatement": { "CountryCodes": ["US", "CA"] }
} } }
]
}
},
"Action": { "Block": {} }
Block or allow a state or province
Some rules follow state lines rather than country borders: online gambling, sweepstakes and some regulated products. Use the two-rule pattern. This example blocks the United States except Oregon and Washington:
- Rule 1 (priority 10, action Count): geo match on
["US"]. It blocks nothing; it labels every request with its country and region. - Rule 2 (priority 11, action Block): AND of a label match on
awswaf:clientip:geo:country:USand a NOT of an OR of label matches onawswaf:clientip:geo:region:US-ORandawswaf:clientip:geo:region:US-WA.
Region data is less accurate than country data, so treat state-level blocks as a best effort and keep the account-level checks (billing address, ID verification) that regulated businesses normally need anyway.
A custom response instead of the default 403
"Is it possible to change the 403 error page on AWS WAF v2?" Yes. Define a custom response body on the web ACL, then reference it from the Block action, with any status code you choose:
"CustomResponseBodies": {
"geo-blocked": {
"ContentType": "TEXT_HTML",
"Content": "<h1>Not available in your region</h1><p>Our courses are licensed for the US, Canada and the UK only.</p>"
}
}
"Action": {
"Block": {
"CustomResponse": {
"ResponseCode": 451,
"CustomResponseBodyKey": "geo-blocked"
}
}
}
Start in Count mode
Before a new geo rule blocks real customers, set its action to Count for a day or two. Count mode records what the rule would have blocked without blocking it. Check the rule's CloudWatch metric and the web ACL's sampled requests for surprises: your own monitoring service in another country, a payment provider's webhooks, a search engine crawler you depend on, or customers on a corporate VPN. Then switch to Block.
Webhooks deserve special attention. Payment processors, email providers and chat platforms call your endpoints from their own servers, which may be anywhere. If a geo rule blocks those calls, payments stop being confirmed and nobody notices until customers complain. Exclude webhook paths from the geo rule, or allow the provider's published IP ranges with an IP set.
WAF geo blocking with Terraform and the CLI
A complete Terraform example: a CloudFront-scope web ACL that allows only three countries, with a custom page, attached to a distribution. CloudFront web ACLs must be created in US East (N. Virginia), so the resource uses a provider for us-east-1:
provider "aws" {
alias = "use1"
region = "us-east-1"
}
resource "aws_wafv2_web_acl" "geo" {
provider = aws.use1
name = "courses-geo"
scope = "CLOUDFRONT" # "REGIONAL" for ALB, API Gateway, AppSync, Cognito
default_action {
allow {}
}
custom_response_body {
key = "geo-blocked"
content_type = "TEXT_HTML"
content = "<h1>Not available in your region</h1>"
}
rule {
name = "allow-only-us-ca-gb"
priority = 0
action {
block {
custom_response {
response_code = 451
custom_response_body_key = "geo-blocked"
}
}
}
statement {
not_statement {
statement {
geo_match_statement {
country_codes = ["US", "CA", "GB"]
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "allow-only-us-ca-gb"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "courses-geo"
sampled_requests_enabled = true
}
}
resource "aws_cloudfront_distribution" "courses" {
# ...
web_acl_id = aws_wafv2_web_acl.geo.arn
}
For an Application Load Balancer or API Gateway REST API, set scope = "REGIONAL", create the web ACL in the same Region as the resource, and attach it with an aws_wafv2_web_acl_association resource instead of web_acl_id.
To check which web ACL a resource uses from the CLI:
# ALB or other regional resource
aws wafv2 get-web-acl-for-resource --resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/shop-alb/0123456789abcdef
# CloudFront: the distribution config shows the web ACL ARN
aws cloudfront get-distribution-config --id E1A2B3C4D5E6F7 --query "DistributionConfig.WebACLId"
If many accounts need the same geo rules, AWS Firewall Manager can push one WAF policy to every account and every new resource in an AWS Organization.
CloudFront geo restriction vs AWS WAF geo match
| CloudFront geo restriction | AWS WAF geo match | |
|---|---|---|
| Price | Free | $5/web ACL + $1/rule a month + $0.60 per million requests |
| Scope | Whole distribution | Any path, host, header or method you combine it with |
| Location detail | Country | Country and region (state or province) |
| Unknown location | Served | Labeled XX; you decide |
| IP exceptions | No | Yes, with IP sets |
| Error page | Custom error response for 403 | Custom response body and any status code |
| Other actions | Block only | Block, Allow, Count, CAPTCHA, Challenge; rate limits per country |
| Works on | CloudFront only | CloudFront, ALB, API Gateway REST, AppSync, Cognito, App Runner and more |
| Metrics | 403s in CloudFront logs, not separated by cause | Per-rule CloudWatch metrics, sampled requests, full logs with labels |
Use both only if you understand the order: geo restriction runs first, and the requests it blocks never reach WAF, so they do not show up in WAF metrics or logs. If you want one place that shows every blocked request with its reason, do the geo blocking in WAF and leave CloudFront geo restriction off. If you already pay for a web ACL on the distribution (most production sites do), adding a geo rule costs just $1 a month more, and you get all of WAF's flexibility.
Ethan: "For the cooking client I used WAF. They wanted the free sample lessons visible everywhere and the paid videos licensed to three countries. Geo restriction would have blocked the whole site."
Geo blocking an ALB, API Gateway, NLB or EC2 instance
Application Load Balancer
"AWS ALB geo blocking" is straightforward: create a regional web ACL in the ALB's Region, add the geo match rule, and associate the web ACL with the load balancer. If the ALB sits behind CloudFront, do the geo blocking on CloudFront instead, and make sure nobody can bypass CloudFront by calling the ALB directly: allow inbound HTTPS on the ALB's security group only from the AWS-managed prefix list com.amazonaws.global.cloudfront.origin-facing, and optionally require a secret custom header that CloudFront adds. Otherwise a blocked visitor who finds the ALB's DNS name simply walks around your geo block. Our ALB vs NLB guide covers the load balancer side.
API Gateway
REST APIs accept a regional WAF web ACL on a stage, so the geo match rule works directly. HTTP APIs and WebSocket APIs do not support WAF; put CloudFront in front of them and use geo restriction or a CloudFront-scope web ACL there, then restrict the API so it only accepts requests that come through CloudFront.
S3 static websites
An S3 bucket cannot geo block on its own. Serve it through CloudFront with origin access control (OAC), block public access to the bucket, and apply geo restriction or WAF on the distribution. Without OAC, the bucket's own URL bypasses the geo block. Our OAC guide shows the bucket policy.
Network Load Balancer and EC2 ("can I geoblock an EC2 instance?")
NLBs do not support WAF, and EC2 security groups have no country filter. You have three choices:
- If the traffic is HTTP or HTTPS, put an ALB with WAF, or CloudFront, in front of the instances instead of exposing them directly. This is the simplest and cheapest answer for websites and APIs.
- For any protocol (game servers, SSH, mail, VPN endpoints, custom TCP or UDP), route the traffic through AWS Network Firewall and use its geographic IP filtering. Suricata-compatible rules accept the
geoipkeyword with a direction (src,dst,bothorany) and ISO country codes, for IPv4 and IPv6:
drop ip any any -> $HOME_NET any (msg:"Geo block inbound"; geoip:src,KP,IR; sid:1000001; rev:1;)
# Allow-list style: pass inbound traffic from the US and Canada only,
# with the firewall policy's default action set to a Drop action
pass ip any any -> $HOME_NET any (msg:"Allow US and CA"; geoip:src,US,CA; sid:1000002; rev:1;)
For an allow list, set the firewall policy's default action to drop. IP-to-country mappings change between database updates, and a default drop makes sure an address that moves to another country stops matching your pass rule and is dropped instead of slipping through. If one address is mapped wrongly, add an explicit drop or pass rule for that address with a higher priority.
- On the instance itself, a host firewall (Windows Defender Firewall, iptables or nftables with ipset) can block lists of country IP ranges. It works for a single server, but you have to download and refresh the country lists yourself, the rule sets get large, and the traffic still reaches your instance. Treat it as a last resort.
Network Firewall costs $0.395 per endpoint-hour (about $288 a month per Availability Zone) plus $0.065 per GB in US East, so it is worth it when you also need its other features. Our Network Firewall vs WAF guide walks through the architecture.
Route 53 geolocation vs geoproximity vs geo restriction
"CloudFront geo restriction vs Route 53 geolocation" and "geoproximity vs geolocation" come up because all three involve location. Only one of them blocks anything.
- Route 53 geolocation routing answers DNS queries differently based on where the query comes from: continent, country, or US state. You might send European visitors to a Frankfurt server and everyone else to Virginia, or serve a localized site per country. You should always add a default record; without one, users from locations you did not list get no answer at all, which looks like an outage.
- Route 53 geoproximity routing sends users to the nearest resource by geographic distance, and lets you widen or shrink the area each resource serves with a bias value. It is about balancing traffic between regions, not about countries.
- CloudFront geo restriction and WAF geo match actually refuse requests.
Why DNS is not a block: DNS only tells a browser where to connect. A visitor can use a different DNS resolver, query from another location, or connect to the IP address or hostname directly. Route 53 also sees the location of the DNS resolver (and, where resolvers send it, part of the visitor's IP address), which is not always the visitor's location. Use geolocation routing to send people to the right place; use CloudFront or WAF to keep people out. See our Route 53 guide for routing policies.
Showing different content by country without blocking
Often you do not want to block a country at all; you want to show its visitors a different price, language, store or legal notice. CloudFront can add viewer location headers to requests, based on the viewer's IP address:
CloudFront-Viewer-Country(two-letter ISO code) andCloudFront-Viewer-Country-NameCloudFront-Viewer-Country-RegionandCloudFront-Viewer-Country-Region-Name(first-level subdivision, such as a US state)CloudFront-Viewer-City,CloudFront-Viewer-Postal-Code,CloudFront-Viewer-Metro-Code(US only) andCloudFront-Viewer-Time-ZoneCloudFront-Viewer-LatitudeandCloudFront-Viewer-LongitudeCloudFront-Viewer-ASNandCloudFront-Viewer-Address(origin request policy only)
Add the headers you need to an origin request policy to pass them to your origin, or to a cache policy if your origin returns different content per country (so CloudFront caches each country's version separately). City, postal code and metro code are not available for every address, and several of the more detailed headers are not added to requests that originate from inside the AWS network.
For a lightweight redirect at the edge, a CloudFront Function on the viewer request event can read the country header (include CloudFront-Viewer-Country in the distribution's origin request policy so the function receives it):
async function handler(event) {
var request = event.request;
var headers = request.headers;
var country = headers['cloudfront-viewer-country'] ? headers['cloudfront-viewer-country'].value : '';
// Send UK visitors to the UK store, once, on the home page
if (country === 'GB' && request.uri === '/') {
return {
statusCode: 302,
statusDescription: 'Found',
headers: { location: { value: 'https://' + headers.host.value + '/uk/' } }
};
}
return request;
}
Redirect only the landing page, and always give visitors a way to switch country. Forcing everyone into a country version based on IP address annoys travelers and expatriates, and it can stop search engines, which mostly crawl from the United States, from seeing your other versions.
Geo blocking pricing, with examples
- CloudFront geo restriction: no additional charge. You pay standard CloudFront prices for requests and data transfer; see our CloudFront pricing guide.
- AWS WAF: $5 per web ACL per month, $1 per rule per month, and $0.60 per million requests inspected. A geo match statement uses 1 WCU, well inside the 1,500 WCUs included with each web ACL.
- AWS Network Firewall: $0.395 per endpoint-hour plus $0.065 per GB processed (US East).
- CloudFront flat-rate plans: the Free ($0), Pro ($15), Business ($200) and Premium ($1,000) monthly plans bundle AWS WAF and DDoS protection with CloudFront, and requests blocked by WAF or DDoS protection do not count against the plan's usage allowance.
Example 1: a small site on CloudFront
A course site with 3 million requests a month uses CloudFront geo restriction with an allow list. Extra cost for geo blocking: $0.
Example 2: the same site with WAF
One web ACL with the geo rule and an AWS managed rule group for common attacks (counted as rules for pricing), 3 million requests a month:
- Web ACL: $5.00
- Rules: 2 x $1.00 = $2.00
- Requests: 3 x $0.60 = $1.80
- Total: about $8.80 a month
At 50 million requests a month, the request charge becomes $30 and the total about $37. Some AWS managed rule groups (Bot Control, account takeover and fraud prevention) add their own subscription and per-request fees on top.
Example 3: blocking saves money too
If 20% of a site's 50 million monthly requests come from bots in countries where it has no customers, a geo block at the edge stops those requests before they reach the origin. That is 10 million fewer requests hitting your load balancer, Lambda functions or database, which usually saves far more than the $1 rule costs.
How to test geo blocking, and where to see blocked requests
Geo blocking checks the IP address the request comes from, so you cannot fake a country with a header (unless you configured WAF to trust X-Forwarded-For, which is exactly why that option needs care). To test, send a request from an address in a blocked country:
- A small EC2 instance or AWS CloudShell in another Region. AWS addresses in, say, the Frankfurt Region usually geolocate to Germany. Launch a tiny instance there, run one
curl, and terminate it. - A VPN service with servers in the countries you want to test.
- A colleague or a monitoring service in another country.
curl -sI https://courses.example.com/
HTTP/2 403
server: CloudFront
x-cache: Error from cloudfront
...
A 403 with x-cache: Error from cloudfront and no response from your origin means CloudFront refused it. If you used a WAF custom response with 451, you see HTTP/2 451 and your own page. Remember that CloudFront caches error responses for the error caching minimum TTL (10 seconds by default), so a retest immediately after a change can show the old result.
Seeing what was blocked
- CloudFront geo restriction: in CloudFront standard logs, blocked requests have
sc-status403. Logs alone cannot tell a geo block from any other 403, so if you need that distinction, run the client IP column (c-ip) through a geolocation database, or move the geo blocking to WAF. - AWS WAF: each rule has CloudWatch metrics (
BlockedRequests,CountedRequests) under its metric name; the console shows sampled requests with the country of each; and WAF logs (to CloudWatch Logs, S3 or Data Firehose) record the terminating rule and the geo labels of every request. - Network Firewall: alert logs show each dropped flow with the rule's message and SID.
Jake: "So you can tell exactly how many people from each country got blocked?"
Ethan: "With WAF, yes. The client wanted a monthly number for the licensing partner. WAF logs gave it to them in one query."
How accurate is geo blocking? VPNs, travelers and edge cases
Country-level IP geolocation is very good: about 99.8% overall for CloudFront's database, and MaxMind, which AWS WAF and Network Firewall use, reports very high country-level accuracy that varies by country and type of address. City and state data is noticeably less precise. The remaining errors come from predictable places:
- VPNs and proxies. A VPN user appears in the VPN server's country. Geo blocking stops casual access, not a determined visitor. If a contract demands more, licensors usually also expect account-level checks such as billing country.
- Corporate networks. Employees of a global company may browse through a gateway in another country, so a London office can appear to be in Ireland or the Netherlands.
- Mobile carriers and satellite internet. Some route traffic through gateways in neighboring countries; satellite users may appear in the country of the ground station.
- Newly reassigned address blocks. When IP ranges move between organizations or countries, databases take time to catch up. Users can request corrections from the database provider; MaxMind runs a correction form.
- Travelers. Your own customers abroad, like Jake on holiday, are blocked from content they may have paid for. Decide whether that is acceptable, and say so on your error page.
- Search engines and services. Crawlers, uptime monitors, payment webhooks and link previews come from data centers that may be outside your allowed countries. Blocking Googlebot from a whole site because it crawls from the US is not usually a problem for a US allow list, but a block list that includes the US would remove your site from search.
Geo blocking is also not a compliance guarantee by itself. Regulators and licensors generally expect "reasonable measures," which is what IP geolocation provides; for anything stricter, combine it with account verification.
"Configured to block access from your country": if you are the visitor
Many people searching this message are not running AWS at all; they are trying to download something. If you see "The Amazon CloudFront distribution is configured to block access from your country," here is what it means:
- The website owner chose to block your country (or failed to allow it) in their CloudFront settings. It is not your computer, your browser or your internet provider, and it is not a virus. CloudFront is the content delivery network many websites use.
- It is based on your IP address. If you are traveling, on a work VPN or behind a corporate network, your address may appear to be in a different country from where you are.
- What you can do: turn off a VPN or work proxy if you use one and try again; try an official mirror or another download source the project offers; or contact the website owner, especially if you are a paying customer, and give them the Request ID from the error page. Getting around a block with a VPN may break the site's terms of use, so the clean fix is always the owner's.
- "How do I turn off geo blocking?" As a visitor, you cannot; only the site owner can change it. As the owner, set the distribution's geographic restrictions to No restrictions (restriction type
none) or remove the WAF geo rule.
Jake's database installer was on a site that had blocked a list of countries by mistake after a configuration change; he downloaded the same installer from the project's official mirror list in two minutes. Our CloudFront 403 "could not be satisfied" guide covers the other causes of that error page.
Common mistakes
- Using
UKinstead ofGB, and forgetting territories such as Puerto Rico (PR). - Leaving the origin reachable directly. An ALB, API or S3 bucket that accepts requests from anywhere makes the CloudFront geo block optional. Use OAC for S3 and the CloudFront origin-facing prefix list (or VPC origins) for load balancers.
- Expecting geo restriction to cover one path. It covers the whole distribution; use WAF for paths.
- Blocking webhooks and monitors. Exclude webhook paths or allow provider IP ranges.
- Forgetting unknown locations. CloudFront geo restriction serves them; a WAF NOT-geo rule blocks them. Decide which you want.
- Trusting
X-Forwarded-Forwithout a trusted proxy. Anyone can send that header; a forwarded IP configuration on an internet-facing resource lets attackers pick their own country. - Using Route 53 geolocation as a block. DNS routing redirects; it does not refuse.
- Using both geo restriction and WAF, then wondering why WAF shows no geo blocks. Geo restriction runs first and those requests never reach WAF.
- A single-page-app error rule turning 403 into 200. Blocked visitors then get your app shell instead of a clear message.
- Switching straight to Block. Run the rule in Count mode first and read the sampled requests.
Frequently asked questions
How do I block a country in AWS?
For content behind CloudFront, turn on geographic restrictions with a block list (or an allow list of the countries you serve). For more control, or for ALBs, API Gateway REST APIs and other HTTP resources, add an AWS WAF rule with a geo match statement. For non-HTTP traffic, use AWS Network Firewall geoip rules.
How do I block a country with AWS WAF?
Create a rule with a geo match statement listing the country codes and set its action to Block. To allow only some countries, wrap the geo match in a NOT statement. Test in Count mode first.
Does CloudFront have geo restriction?
Yes. Every CloudFront distribution has a geographic restrictions setting on its Security tab, with an allow list or a block list of countries. It applies to the whole distribution and returns HTTP 403 to blocked viewers.
How much does CloudFront geo restriction cost?
Nothing extra. You pay only the standard CloudFront charges. AWS WAF geo blocking costs $5 per web ACL and $1 per rule a month, plus $0.60 per million requests.
What is the difference between CloudFront geo restriction and AWS WAF?
Geo restriction is free, country level and applies to the whole distribution. WAF geo match costs a little, works on many resource types, supports regions and paths, IP exceptions, custom responses and detailed logs.
What country codes does CloudFront geo restriction use?
ISO 3166-1 alpha-2 two-letter codes, such as US, CA, GB (not UK), DE and HK.
Can AWS WAF block by state or region?
Yes. A geo match rule adds region labels (ISO 3166-2, such as US-OR), and a second rule with label match statements blocks or allows by region.
How accurate is the AWS WAF geographic match rule?
Country-level accuracy is very high but not perfect, and it varies by country and type of address. VPNs, proxies and corporate gateways make visitors appear in other countries. Region and city data is less precise.
Is Route 53 geolocation the same as geo blocking?
No. Geolocation routing returns different DNS answers depending on the user's location, but it does not refuse anyone. Use CloudFront geo restriction or AWS WAF to block.
Can I geo block an EC2 instance or a Network Load Balancer?
Not with security groups. Put HTTP traffic behind an ALB with WAF or behind CloudFront, or route any protocol through AWS Network Firewall and use its geoip rules.
Can I geo block only one path in CloudFront?
Not with geo restriction, which covers the whole distribution. Use an AWS WAF rule that combines a URI path match with a geo match, or use separate distributions.
How do I allow some IP addresses from a blocked country?
Use AWS WAF: block requests that are NOT (from an allowed country OR in your IP set). CloudFront geo restriction cannot make IP exceptions.
Why is CloudFront blocking me from websites?
The website owner has restricted access by country, and your IP address appears to be in a blocked country, often because of travel, a VPN or a corporate network. Only the owner can change it; try an official mirror or contact them.
How do I turn off geo blocking in CloudFront?
Edit the distribution's geographic restrictions and choose No restrictions (restriction type none in the API, CLI, Terraform or CloudFormation), then wait for the deployment to finish.
Do geo-blocked requests reach my origin?
No. CloudFront geo restriction and a WAF web ACL on CloudFront both refuse the request at the edge location, so your origin never sees it. A web ACL on an ALB or API Gateway blocks the request before it reaches your targets or integrations.
Can I change the error page for geo-blocked visitors?
Yes. In CloudFront, add a custom error response for 403. In AWS WAF, use a custom response body and status code (for example 451) on the Block action.
For readers preparing for an AWS certification
We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.
Exams that test this topic: Security – Specialty (SCS-C03) • Solutions Architect – Associate (SAA-C03) • CloudOps Engineer – Associate (SOA-C03).
Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:
1. A media company may distribute its videos only in three countries. All content is served through CloudFront. What is the simplest way to enforce this at no extra cost?
CloudFront geographic restrictions with an allow list of the three countries.
2. Only the /checkout path of a website behind CloudFront must be limited to two countries; the rest stays open. What should be used?
An AWS WAF rule that combines a URI path match with a geo match statement (in a NOT statement).
3. A company must block requests from one country to an Application Load Balancer. Which service supports this?
AWS WAF with a geo match rule in a regional web ACL associated with the ALB.
4. Users can reach an S3 origin directly and bypass the CloudFront geo restriction. What prevents this?
Origin access control (OAC) with S3 Block Public Access, so only CloudFront can read the bucket.
5. A game server behind a Network Load Balancer must drop traffic from certain countries. Which AWS service can filter it by country?
AWS Network Firewall with geoip rules (Suricata-compatible).
6. Which Route 53 routing policy returns different answers based on the user's country, and does it block access?
Geolocation routing; it routes users but does not block them.
7. In which Region must an AWS WAF web ACL for a CloudFront distribution be created?
US East (N. Virginia), us-east-1 (the Global/CloudFront scope).
Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.
The licensing partner checked a month later. The WAF logs showed the paid video paths had answered only US, Canadian and UK addresses, about 4,000 requests from elsewhere had received the "not available in your region" page, and the free sample lessons had been watched in 61 countries, which the marketing team loved. Jake, back from holiday, asked whether his own blog needed geo blocking. "Only if your lawyer says so," Ethan replied. "For a blog, the whole world is the audience."
📌 If you keep one line from this page
Use CloudFront geo restriction (free, whole distribution, country level) when "only these countries" is all you need; use an AWS WAF geo match rule ($1 a month per rule on an existing web ACL) when you need paths, states, IP exceptions, custom pages or resources other than CloudFront; and lock down your origin so nobody can walk around the block.
GB, not UK · Count mode first · Route 53 routes, it does not block.
Revision note. Written October 21, 2026. Prices are US East list prices on that date. We will update this page when CloudFront or AWS WAF geo features or prices change.