S3 PermanentRedirect Error: Find the Region and Fix It
PermanentRedirect — "The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint." — means the bucket is in a different Region than the one your request went to (an endpoint is just the web address a request is sent to). Look up the bucket's real Region with aws s3api head-bucket --bucket biscuit-repair-photos --query BucketRegion --output text, then send every request there: --region in the CLI, the Region on your SDK client, or the Regional endpoint in your URL. The twist: nothing moved. A bucket can never change Region, so S3 is telling you where to knock, not announcing that your bucket relocated.
Jake's booking page went blank at 9 a.m. on a Saturday, right as people were lining up to rebook cracked-screen repairs. The photo gallery on it came from a Lambda function (a small piece of code AWS runs for you on demand), and its log held one red line: PermanentRedirect. A week earlier he had moved that function from Oregon to N. Virginia "to be closer to customers." The photos, in a bucket named biscuit-repair-photos after his cat, stayed exactly where they were.
"So S3 lost my bucket?" Jake asked Ethan, who was halfway through a samosa. "No," said Ethan. "Your function moved. The bucket is where you left it, and S3 is polite enough to tell you so. Buckets don't move, Jake. They're houses, not vans." What follows is that conversation stretched into a full repair manual: the fast fix, every cause, the console and CLI versions, and what to do when the fast fix fails.
PermanentRedirect: what S3 is actually telling you
The error arrives as HTTP status 301 Moved Permanently with the error code PermanentRedirect. Every tool dresses it a little differently, so here are the versions people actually paste into search boxes:
An error occurred (PermanentRedirect) when calling the ListObjectsV2 operation:
The bucket you are attempting to access must be addressed using the specified endpoint.
Please send all future requests to this endpoint.
Aws::S3::Errors::PermanentRedirect
Status Code: 301; Error Code: PermanentRedirect
S3 Error Code: PermanentRedirect. S3 Error Message: The bucket is in this region:
us-east-2. Please use this region to retry the request
They all say the same thing. Every bucket lives in exactly one Region (a Region is a cluster of AWS data centers in one part of the world), and every Region has its own S3 front door, an endpoint like s3.us-west-2.amazonaws.com. Knock on the wrong front door and S3 answers with a 301 that points you at the right one.
The word "permanent" is about the address you used, not about the bucket. A permanent redirect means your request addressed the resource inappropriately, and it will keep being wrong until you change the address. Meanwhile the bucket's name, owner and Region are all fixed at creation. You can't edit any of them later, and objects stay in the bucket's Region unless you explicitly copy them elsewhere.
That's why the error feels so alarming and is so harmless. Your data is fine. Your request just went to the wrong place.
🙋♂️ Jake's Reality Check
"If S3 already knows where my bucket is, why doesn't it just forward my request?"
The straight answer. It tells you where to go instead of carrying the request across. Some SDKs will follow that hint for you if you switch the option on, but the default in the ones I cover below is to stop and report the error. Ethan's view: "Don't rely on the hint. Put the right Region in your config and the error never happens."
The two-minute fix: find the Region, then use it
- Find the bucket's real Region. Console: open the S3 console, choose General purpose buckets, and read the Region shown beside your bucket's name in the list. CLI: run
aws s3api head-bucket --bucket biscuit-repair-photos --query BucketRegion --output textand it prints something likeus-west-2. - Hand that Region to the thing that failed. CLI: add
--region us-west-2. SDK: set the Region on the client. Raw URL: use the Regional form,https://biscuit-repair-photos.s3.us-west-2.amazonaws.com/key. - Rerun the exact same call. If it worked, you're done with the error. Make it stick by putting the Region in your profile, environment variable or client constructor, not in your memory.
- If it still redirects, something is overriding your Region: a pinned endpoint, an environment variable, or a copy command that needs a second Region flag. The sections on causes and failure modes below walk through each one.
aws s3api head-bucket --bucket biscuit-repair-photos --query BucketRegion --output text
aws s3 ls s3://biscuit-repair-photos --region us-west-2
If head-bucket itself throws an error instead of printing a Region, don't panic. A HeadBucket call to any bucket name, in any Region of the same partition, comes back with a response header naming the bucket's location regardless of the permissions on the bucket. A partition is a grouping of Regions; the standard commercial Regions are one partition, and the China and GovCloud Regions are others. If your CLI version doesn't print the Region for you, add --debug and look for an x-amz-bucket-region header in the response. Headers are where that value travels; how each tool surfaces them varies.
One more place to look before you run anything: the error itself. The Lambda version of the message names the Region outright ("The bucket is in this region: us-east-2"). The raw XML error carries an Endpoint element, such as BUCKET.s3.us-west-2.amazonaws.com, and the Ruby SDK's error object exposes endpoint, bucket and region fields. If your logging cuts the message short after "specified endpoint", you've thrown away the answer. Log the whole error body, not just the first line.
After you rerun the command, here is what "fixed" looks like: the same call that returned PermanentRedirect now returns data or a normal success message, with no error text at all. If it returns a different error, say AccessDenied or NoSuchKey, that is good news in a strange costume. It means the request reached the right bucket, and you're now debugging permissions or a missing file, which are separate problems with their own fixes.
Which one are you? A one-question triage
Before you read every cause, answer one question: what was making the request? That single answer narrows a dozen possibilities to two or three.
| What sent the request | Where the Region comes from | Go to |
|---|---|---|
| You, typing in a terminal | Flag, environment variable or profile | Causes 1, 3 and 5 |
| Your application code | The client constructor | Causes 1 and 2, then the SDK table |
| A link in a browser or an email | The hostname in the URL | Cause 4 and the endpoint table |
| A deploy tool (SAM, CloudFormation, CI) | The function or stack's Region | Cause 6 |
| A CDN or another AWS service in front | The origin or target setting | The CloudFront section |
Jake's gallery is the second row: application code whose client Region followed the function to Virginia while the bucket stayed in Oregon. Ethan's shortcut: "Whoever built the request chose a Region, even if they chose it by doing nothing. Find that choice." A default is still a choice, and it's the one people forget they made. A script that has worked untouched for two years can start failing the day someone exports a different Region in a shell profile, and the script never changed at all.
How to find an S3 bucket's Region (console, CLI and SDK)
There are more ways to ask "which Region is this bucket in?" than there should be, and they are not equal. Here they are, best first.
| Method | How | Works without bucket permissions? |
|---|---|---|
| HeadBucket (the best default) | aws s3api head-bucket --bucket NAME --query BucketRegion --output text |
Yes. The Region header comes back whether or not access is authorized. |
| Console list | S3 → General purpose buckets → the Region shown in the list | Only for buckets in your own account. |
| ListBuckets (paginated) | aws s3api list-buckets --max-items 100 --page-size 100 |
Only your own account's buckets. Paginated responses include each bucket's Region. |
| Python SDK | client.head_bucket(Bucket=NAME)["BucketRegion"] |
Same HeadBucket rule applies. |
| The bucket's own name | Only for account regional namespace buckets, where the name ends in -us-west-2-an style |
Yes, it's just text. Ordinary global names tell you nothing. |
| GetBucketLocation | aws s3api get-bucket-location --bucket NAME |
Not reliably. See the note just below. |
Here is where the popular advice goes stale. Nearly every tutorial on this error tells you to run get-bucket-location. That operation is no longer a best practice. In the bucket's home Region it's governed by the bucket policy. In other Regions the bucket policy doesn't apply, which means a cross-account caller won't be authorized there. HeadBucket has none of those problems, which is why it's the one I use.
One more trick for the CLI: list-buckets accepts a --bucket-region filter, which lists only the buckets in one Region. Send that request to an endpoint in the same Region you're filtering on. A mismatch isn't supported, so --region us-east-2 --bucket-region us-east-2 is the shape to copy.
Seven causes of PermanentRedirect, from most to least likely
In order of how often I'd expect each one to bite, cheapest fix first:
| Cause | How you can tell | Fix |
|---|---|---|
| 1. Your client's Region is wrong (default Region, profile or environment variable) | aws configure list shows a Region that differs from the bucket's |
Add --region, or fix the profile or environment variable |
| 2. You moved the code, not the bucket (Lambda, EC2, container in a new Region) | It worked until the deploy moved to another Region | Set the client Region to the bucket's, or copy the data to a bucket in the new Region |
| 3. A bucket-to-bucket copy with only one Region flag | Only aws s3 cp or sync between two buckets fails |
Add --source-region (details below) |
| 4. A path-style URL to the legacy global endpoint | URL looks like s3.amazonaws.com/BUCKET/key and the bucket is outside N. Virginia |
Use the Regional, virtual-hosted form |
5. A pinned endpoint (--endpoint-url or SDK endpoint setting) in the wrong Region |
Changing --region changes nothing |
Remove the pin, or point it at the bucket's Regional endpoint |
| 6. A deployment bucket in a different Region than the Lambda function | The message says "The bucket is in this region" during a deploy | Use one artifact bucket per Region |
| 7. The name belongs to someone else (typo, or a name shared by coincidence) | The Region HeadBucket reports isn't one you ever used | Check spelling, and pass the expected bucket owner |
Cause 1 and cause 2 together account for most of the pain, and they're the same mistake seen from two angles: the bucket's Region is a fact about the bucket, while your client's Region is a setting about you. They only match by accident or by care.
🕐 What changed between versions
- Before: a general purpose bucket name was unique across every account in every Region of the partition, and the name said nothing about where the bucket lived.
- Now: since March 2026 you can create buckets in your account regional namespace, where the name is your prefix plus your 12-digit account ID, the Region code and
-an, for exampleamzn-s3-demo-bucket-111122223333-us-west-2-an. Those names carry their own Region. Global names still exist and still don't. - What that means here: for new buckets, the name can answer the Region question, and a CloudFormation template can build it with
${AWS::Region}. Older buckets still need the HeadBucket lookup.
Fixing it in code: CLI, Python, Java and JavaScript
There are two ways to fix this in an application: tell the client the right Region up front, or switch on the SDK's redirect following so it retries in the Region S3 names. The first is the one I want you to use. The second is a safety net.
| Tool | Set the bucket's Region | Or follow redirects |
|---|---|---|
| AWS CLI | --region us-west-2, the AWS_REGION or AWS_DEFAULT_REGION variable, or the profile's region setting |
No switch to rely on. Set the Region. |
| Python (boto3) | boto3.client("s3", region_name="us-west-2"), or AWS_DEFAULT_REGION |
No switch to rely on. Look up the Region with HeadBucket and build the client with it. |
| Java SDK 2.x | .region(Region.US_WEST_2) on the client builder |
.crossRegionAccessEnabled(true), available from version 2.20.111. Off by default. |
| JavaScript SDK v3 | new S3Client({ region: "us-west-2" }) |
followRegionRedirects: true in the client config. Opt-in. |
| Inside Lambda | Pass the Region to the client constructor. AWS_REGION is reserved and can't be overridden. |
Same flags as the SDK you use. |
# Python: ask S3 where the bucket lives, then build the client there
import boto3
probe = boto3.client("s3", region_name="us-east-1")
region = probe.head_bucket(Bucket="biscuit-repair-photos")["BucketRegion"]
s3 = boto3.client("s3", region_name=region)
// JavaScript v3: safety net on top of a sensible Region
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-west-2", followRegionRedirects: true });
// Java 2.x: same idea
S3AsyncClient client = S3AsyncClient.builder()
.crossRegionAccessEnabled(true)
.build();
When the Java option is on, the SDK sends the request to the client's configured Region first. If the bucket isn't there, the error response carries the actual Region, and the SDK makes a second request to the right one and caches that mapping in the client. So the first call can be slower, and later calls are fine. Turning it on doesn't change who is allowed in: you still need permission to the bucket wherever it lives.
And a trap that catches people who think they already set the Region: the CLI reads --region first, then environment variables, then the profile. CLI v2 checks AWS_REGION before AWS_DEFAULT_REGION, while Boto3 uses AWS_DEFAULT_REGION. So one stray variable in a shell profile can send the CLI to one Region and your Python script to another. Run aws configure list and read the last column: it tells you whether the Region came from the profile, an environment variable or a flag.
✅ Why this is the one to use
Set the Region explicitly and leave redirect following off. A redirect-following client still works when the bucket is somewhere unexpected, but it hides the mistake and adds a round trip. An explicit Region fails loudly the day somebody repoints it, which is exactly when you want to know.
The endpoint trap: s3.amazonaws.com, path-style URLs and legacy hostnames
If you never touch an SDK and only paste URLs, this section is your whole story. S3 has two URL shapes. Virtual-hosted style puts the bucket name in the hostname: https://BUCKET.s3.REGION.amazonaws.com/key. Path-style puts it in the path: https://s3.REGION.amazonaws.com/BUCKET/key. Both work in every Region today, though path-style is slated to be discontinued at some point in the future (the plan was delayed, with the update dated September 23, 2020). Virtual-hosted is the safer habit.
Then there's the old hostname without a Region in it, s3.amazonaws.com, called the legacy global endpoint. It exists for backward compatibility, and it's behind a surprising number of 301s.
| What you sent | Bucket's Region | What S3 does |
|---|---|---|
BUCKET.s3.REGION.amazonaws.com |
That same Region | Works. This is the standard form. |
s3.amazonaws.com/BUCKET/key (path-style, global) |
US East (N. Virginia) | Works. The legacy global endpoint lands in N. Virginia. |
s3.amazonaws.com/BUCKET/key (path-style, global) |
Any other Region, for example US West (Oregon) | HTTP 301 Permanent Redirect, with a message naming the correct URI. |
BUCKET.s3.amazonaws.com (virtual-hosted, global) |
A Region launched before March 20, 2019 | DNS is updated to reroute, which can take time. Meanwhile the request goes to N. Virginia and S3 answers with a 307 Temporary Redirect. |
BUCKET.s3.amazonaws.com (virtual-hosted, global) |
A Region launched after March 20, 2019 | No redirect at all. HTTP 400 Bad Request. |
BUCKET.s3-us-west-2.amazonaws.com (dash instead of dot) |
Some older Regions | A legacy form you may see in logs. Use the dot form, s3.us-west-2. |
Read the table top to bottom and the lesson writes itself: the only hostname that works for every bucket everywhere is the one with the bucket's own Region in it. The newer your bucket's Region, the less the global hostname will forgive you.
A few neighbors of this problem, so you can rule them in or out fast. Dual-stack endpoints (the ones that speak both IPv4 and IPv6) always include the Region in the name. A custom domain works through a CNAME record (a DNS alias) that must point at BUCKET.s3.REGION.amazonaws.com for the bucket's own Region, with the bucket named exactly like the hostname, and the record defined exactly in that shape or it may appear to work and then misbehave later. Virtual-hosted HTTPS with a bucket name containing dots trips the wildcard certificate, which is a certificate error, not a redirect. And if someone opens the bare website endpoint with no bucket name in it, S3 sends a different 301, to the S3 product page. That one is about a missing bucket name, not a Region.
🙋♂️ Jake's Reality Check
"My old links to the gallery photos all have s3.amazonaws.com in them. Are they about to die?"
The straight answer. Not automatically. If the bucket is in N. Virginia, the global hostname keeps landing in the right place. If the bucket is anywhere else and the links are path-style, they already return the 301. Test a link from each bucket, and rewrite the ones that redirect into the virtual-hosted Regional form.
Where it hides: Lambda, CloudFormation, CloudFront and VPC endpoints
Plenty of people meet this error without ever typing an S3 command. Something else is calling S3 on their behalf.
Lambda and SAM deployments
When you upload a function's deployment package from an S3 bucket, the bucket must be in the same Region as the function. Break that rule in UpdateFunctionCode, or with the package and deploy commands in the AWS CLI or the SAM CLI, and you get this:
An error occurred (InvalidParameterValueException) when calling the UpdateFunctionCode operation:
Error occurred while GetObject. S3 Error Code: PermanentRedirect.
S3 Error Message: The bucket is in this region: us-east-2.
Please use this region to retry the request
The region in that message is the bucket's. Read it literally: either deploy the function in that Region or use a bucket in the function's Region. The habit that prevents this for good is one deployment artifact bucket per Region where you build. CLI route: aws lambda update-function-code --function-name order-texts --s3-bucket my-deploys-us-east-1 --s3-key build.zip --region us-east-1. And remember that inside a running function, AWS_REGION is reserved for the runtime, so the fix for a function reading a bucket elsewhere goes in the client constructor, not in an environment variable.
CloudFormation
The S3Bucket property of a Lambda function's code must name a bucket in the function's Region (it may belong to a different account). Also note that changes to a package in S3 aren't detected automatically during stack updates, so change the object key or version to force a new deploy. For buckets you create in the same template, Fn::GetAtt gives you two domain names: DomainName returns the global style, like BUCKET.s3.amazonaws.com, and RegionalDomainName returns the Regional style, like BUCKET.s3.us-east-2.amazonaws.com. Use RegionalDomainName for CloudFront origins.
CloudFront, and its 307 cousin
If a CloudFront distribution in front of a bucket returns a redirect, read the status code before anything else. A 307 Temporary Redirect on a bucket you created recently is a different animal from our 301. After you create a bucket, it can take up to 24 hours before the bucket name propagates across all Regions, and during that window requests to Regional endpoints outside the bucket's Region can get the 307. CloudFront forwards to the default S3 endpoint, s3.amazonaws.com, which is in us-east-1, so a new bucket elsewhere trips it. The fix is to set the origin domain to the Regional form, BUCKET.s3.us-west-2.amazonaws.com. If the bucket is configured as a website, the origin is the website endpoint instead, BUCKET.s3-website.us-west-2.amazonaws.com. The general tip applies everywhere: specify the bucket's Region in every request, and you skip the DNS propagation wait entirely.
VPC endpoints
A gateway endpoint (a private side door from your VPC to S3) serves buckets in the VPC's own Region. Reaching a bucket in a different Region takes another path: an interface endpoint with cross-Region PrivateLink, a NAT gateway, an internet gateway, and so on. Each one has its own price, covered in the cost section. If a private subnet suddenly can't reach a bucket in another Region, the endpoint is the first suspect, well before the 301.
Third-party tools and connectors
Backup software, Spark and Hadoop connectors, workflow engines and S3-compatible storage tools often have a box labeled "endpoint", "storage URL" or "service URL". Whatever is typed there is a pinned endpoint, and it beats every Region setting you made elsewhere. Paste https://s3.amazonaws.com into that box and point the tool at a bucket outside N. Virginia, and you have rebuilt the legacy-global-endpoint problem from the endpoint section. The fix is the same: clear the box so the tool builds the endpoint from its Region setting, or type the bucket's Regional endpoint, https://s3.us-west-2.amazonaws.com style. If the tool is one of those that must talk to other vendors' storage as well, it may insist on a custom endpoint; then the Region belongs inside the hostname.
Copying between Regions: the --source-region trap
This one deserves its own section because the error looks impossible. You typed the right Region. You typed it in the right place. It still redirects. The reason: for a bucket-to-bucket copy, --region means the destination bucket's Region. If you don't also pass --source-region, the CLI assumes the source is in the same Region as the destination, sends the source request to the wrong front door, and S3 answers PermanentRedirect.
aws s3 sync s3://biscuit-repair-photos s3://biscuit-photos-east \
--source-region us-west-2 --region us-east-1
aws s3 cp s3://biscuit-repair-photos s3://biscuit-photos-east \
--recursive --source-region us-west-2 --region us-east-1
In an SDK, the same idea holds: a copy request goes to the destination bucket, so build the client in the destination bucket's Region. A few details worth knowing before a big copy. The S3 console can copy or move objects across Regions, but it can't do cross-Region copies of objects encrypted with SSE-KMS (server-side encryption using keys from AWS Key Management Service), nor objects encrypted with customer-provided keys; use the CLI or an SDK for those. Transfer Acceleration doesn't support cross-Region copies. And when the copy source is in a different Region, the data transfer is billed to the account that owns the source.
⚠️ What this actually breaks
Running sync or cp --recursive the wrong way round. The first argument is the source and the second is the destination, and a mixed-up order copies your real data over an empty bucket, or worse, over a populated one. Run it once with --dryrun first, and read the list of files it says it would copy.
What it costs: the error is free, the fix might not be
The 301 PermanentRedirect is on the short list of responses S3 doesn't bill the bucket owner for. So is its sibling PermanentRedirectControlError, and so is the 307 TemporaryRedirect. In general, bucket owners are billed for successful 200 responses and for 4XX client errors, with a named list of exceptions, and these redirects are on the exceptions list. The request that follows the redirect is a normal request, billed like any other.
What costs money is what you do about it. Prices below are US East (N. Virginia) and US West (Oregon) as of September 30, 2026: $0.02 per GB for data transferred out of S3 to another Region (Oregon to N. Virginia in this example), $0.0004 per 1,000 GET requests in S3 Standard. Data coming into S3 from the internet is free, and so is data moving from S3 to an AWS service in the same Region as the bucket.
Jake's numbers, so the arithmetic is on the table: the photo bucket holds 120 GB in 48,000 objects in us-west-2. His Lambda in us-east-1 pulls about 30 GB a month to resize for the booking page, in roughly 20,000 GET requests.
| Option | One-time cost | Monthly cost |
|---|---|---|
| A. Point the function at us-west-2 and leave the bucket | $0 | 30 GB × $0.02 = $0.60, plus 20 × $0.0004 = $0.008 for GETs. About $0.61. |
| B. Leave the Region wrong and let the SDK follow redirects | $0 | The same $0.61 in charges, plus an extra round trip whenever a call gets redirected. |
| C. Copy everything to a new bucket in us-east-1 | 120 GB × $0.02 = $2.40 transfer, plus 48 × $0.0004 = about $0.02 for source GETs, plus the copy requests on the destination and storage for both copies while both exist. | $0 in transfer, since S3 to a Lambda in the same Region is free. |
Do the break-even: $2.40 one-time divided by $0.60 a month saved is four months, and a little longer once the destination's request charges and the overlap in storage are counted. For a 120 GB shop bucket, that's a coin flip, and it means the honest recommendation is option A today. Moving buckets to save sixty cents a month is a hobby, not a cost saving.
Jake took the news badly. "I was ready to pay to fix this." Ethan shrugged. "You are paying. You're paying sixty cents a month to keep your photos in Oregon, and you're paying it happily, because Oregon is where they are. The expensive part of this error was the Saturday, not the bytes."
The numbers change when the volume does. One published cost comparison for cross-Region access (prices as of January 2026) puts 1,000 GB a month from us-east-1 to a bucket in us-west-2 at $20.00 on the cheapest network path, and $101.50 through a NAT gateway (a shared outbound door for a private network, which adds $0.045 per GB for processing plus fixed hourly charges). If your traffic is in the terabytes, where it crosses the network matters more than the redirect ever did.
Two smaller bills worth knowing. Browsing a bucket in the S3 console makes GET and LIST requests, and those are billed at the same rate as API requests. And a Multi-Region Access Point, the single global address that routes requests across buckets in several Regions, charges a data routing fee of $0.0033 per GB on top of requests, storage and any cross-Region transfer.
When the Region itself is wrong: moving to a bucket in the right one
Sometimes the Region is honestly the problem: the bucket sits far from everything that uses it, or a rule says data must live somewhere else. You can't edit the Region of a bucket. There is no setting for it and no exception. What you can do is build a bucket in the right Region and move the contents, then retire the old one.
- Create the new bucket in the target Region. For us-east-1,
aws s3api create-bucket --bucket biscuit-photos-east --region us-east-1. For any other Region, add the location:--create-bucket-configuration LocationConstraint=us-west-2. You can name it in your account regional namespace (biscuit-photos-111122223333-us-east-1-an) so the name carries the Region; use the Regional endpoint when you create it. - Copy the objects. Use the cross-Region command from the copy section, with
--source-regionand--region. For a bucket you keep writing to, S3 Replication keeps it in sync going forward, and S3 Batch Replication copies the objects that already exist. - Recreate the bucket settings. Sync copies objects, not configuration. Bucket policy, CORS rules, lifecycle rules, encryption defaults and public access settings are separate things and need setting on the new bucket.
- Repoint everything that talks to the bucket: Lambda environment variables, CloudFront origins, CNAME records, hard-coded URLs, IAM policies that name the old bucket's ARN.
- Compare before you delete.
aws s3 ls s3://OLD --recursive --summarizeand the same for the new bucket should agree on the object count and total size. - Retire the old bucket last, and only when nothing still reads from it.
⚠️ What this actually breaks
Deleting a bucket in the shared global namespace frees its name for anyone to re-create. Another account can take it and potentially receive requests meant for your old bucket. If you want the same name in the new Region, expect a gap, and expect to keep every old link pointing somewhere you control. Buckets in your account regional namespace can never be re-created by another account.
Region and account edge cases the top answers skip
Opt-in Regions and newer Regions
Some Regions are opt-in, meaning your account can't use them until you enable them in the account settings. The standard example for IllegalLocationConstraintException uses ap-east-1, which is one of them. Separately, for any Region launched after March 20, 2019, your client and your bucket must be in the same Region, and the legacy global hostname returns a 400 instead of helping. If a bucket lives in a newer or opt-in Region, pass that Region explicitly every time. CLI route to enable one: aws account enable-region --region-name ap-east-1; console route: Account settings, then the Regions section.
Buckets in someone else's account
A redirect doesn't mean you have access. HeadBucket answers with the bucket's Region whether or not you're allowed in, so the redirect can be followed straight into an AccessDenied. If the owner won't add a bucket policy or an access point for you, there is nothing you can change on your side. And GetBucketLocation, the tool most tutorials reach for, won't authorize cross-account callers in other Regions at all.
Organization rules that turn a 301 into a 403
Fix the Region and the next message may be AccessDenied. One reason: an organization can restrict which Regions people may use with the aws:RequestedRegion condition key in a policy. If your organization allows only us-east-1 and the bucket is in us-west-2, pointing the client at Oregon trades one error for another, and the fix is a policy change, not a flag. That one is for your AWS administrator.
Names that belong to strangers
Bucket names in the global namespace are unique across every account in every Region of the partition. So a name you mistype, or a name you deleted last month, can be somebody else's bucket in a Region you never used, and S3 will happily redirect you toward it. Two guards help. aws s3api head-bucket --bucket NAME --expected-bucket-owner 111122223333 fails if the owner isn't who you expect. And bucket owner condition in your code makes requests fail instead of quietly landing in the wrong place.
Access points
An access point's ARN (its Amazon Resource Name, the long unique label every AWS thing has) already contains a Region: arn:aws:s3:us-west-2:123456789012:accesspoint/myaccesspoint. That's handy, because the Region travels with the name. It's also why an access point ARN pasted into a client configured for another Region is a classic way to reproduce this error in a new disguise.
Errors that look like this one (and what each means)
S3 has a whole family of "wrong door" messages. You may land on this page holding one of the cousins. All of these are on the list of responses the bucket owner isn't billed for.
| Status and code | What it says | What to do |
|---|---|---|
301 PermanentRedirect |
The bucket must be addressed using the specified endpoint. | This page. |
301 PermanentRedirectControlError |
The API operation you are attempting to access must be addressed using the specified endpoint. | Same idea, but for an API operation. Send it to the endpoint it names. |
307 TemporaryRedirect |
You are being redirected to the bucket while DNS is being updated. | New bucket. Use the Regional endpoint; it fades within about 24 hours. |
400 IllegalLocationConstraintException |
You are trying to access a bucket from a different Region than where the bucket exists. | Use the --region option. |
400 IncorrectEndpoint |
The specified bucket exists in another Region. | Direct requests to the correct endpoint. |
400 EndpointNotFound |
Direct requests to the correct endpoint. | Same cure: find the Region, build the endpoint from it. |
400 AuthorizationHeaderMalformed |
The authorization header that you provided is not valid. | Often a signing-Region mismatch in custom code. Sign for the bucket's Region. |
400 InvalidHostHeader |
The host headers provided in the request used the incorrect style addressing. | Switch between path-style and virtual-hosted style as the endpoint requires. |
The last two rows are my interpretation of a short description, so treat the "what to do" column as a starting point for those, not a guarantee. The Region-mismatch ones above them say what they mean in plain words.
It still doesn't work: failure modes by symptom
You set the Region, reran the command, and S3 redirected you again. Here's how to read what you're seeing.
| Symptom | Likely cause | What to do |
|---|---|---|
Changing --region changes nothing |
A pinned endpoint (--endpoint-url, SDK endpoint setting) overrides the Region |
Remove it, or set it to https://s3.BUCKET-REGION.amazonaws.com |
| Only copy or sync between buckets fails | Source assumed to share the destination's Region | Add --source-region |
| Works on your laptop, fails in Lambda or a container | The runtime's Region differs from your laptop's profile | Set the Region in the client constructor |
| Works in the console, fails in the CLI or SDK | The console lists buckets from every Region; the CLI and SDK use your default Region | Read the bucket's Region from the console and pass it |
| Only a brand-new bucket misbehaves | Name propagation, up to 24 hours (a 307, not a 301) | Use the Regional endpoint everywhere |
Redirect gone, now AccessDenied |
Permissions, a bucket policy, or an organization Region restriction | Check the policy, not the Region |
| A long-running Java client keeps redirecting after you recreated a bucket elsewhere | The client keeps a cached Region for each bucket name, so a stale entry is the likely suspect | Build a fresh client (restart the service) |
| The bucket "lives" somewhere you never used | Wrong or stranger-owned name | Recheck spelling; use --expected-bucket-owner |
Jake asked the obvious follow-up: "How do I know I'm not just going round in circles?" Ethan's answer is a rule I like. "Change one thing, rerun the same command, and read the message word by word. If the message changed, you're making progress, even if the command still fails. If it's identical, the thing you changed wasn't the thing in charge."
When nothing works: what to gather before you open a support case
If you've walked every row above and S3 still insists on redirecting, collect this first. It turns a week of back-and-forth into one round.
- The exact bucket name and its owner: your account, another account in your organization, or an outside party.
- The Region S3 reports, from the HeadBucket output or the
x-amz-bucket-regionheader. - The full error text, status code and error code, copied, not retyped.
- The request ID and the extended request ID from the error. SDK exceptions print both; the raw XML error holds a RequestId and a HostId.
- A UTC timestamp of a failing call, and what your client was configured with: the
aws configure listoutput, the SDK name and version, and any endpoint override. - What changed just before it broke: a Lambda moved, a new SDK, a new bucket, a new organization policy.
Be realistic about what a case can do. There is no setting, button or API call that changes a bucket's Region, so support can't move your bucket, and nobody can grant you access to someone else's bucket except its owner. The request IDs matter because they let AWS look at one specific request rather than guess. In almost every case I'd expect the answer to be one of the causes in this post, so the support case is mostly a way of getting a second pair of eyes on your client configuration.
Stop it happening again: a small check and a naming habit
Three habits, cheapest first. First, list every bucket you own with its Region, so the facts live somewhere besides your head:
for b in $(aws s3api list-buckets --query "Buckets[].Name" --output text); do
echo "$b $(aws s3api head-bucket --bucket "$b" --query BucketRegion --output text)"
done
Second, when a service starts up, ask S3 instead of trusting configuration. If the call throws, the Region header travels with the error response, and whether your SDK exposes response headers on exceptions is worth confirming for your version. In Python the pattern looks like this:
from botocore.exceptions import ClientError
try:
region = probe.head_bucket(Bucket=name)["BucketRegion"]
except ClientError as e:
region = e.response["ResponseMetadata"]["HTTPHeaders"].get("x-amz-bucket-region")
Third, put the Region in the name. In CloudFormation, BucketName: !Sub "amzn-s3-demo-bucket-${AWS::AccountId}-${AWS::Region}-an" with BucketNamespace: "account-regional" builds a name that states its own Region and can never be re-created by another account. Mind the length: the suffix counts toward the 63-character limit, so with a 12-digit account ID and a Region like us-east-1 you have 37 characters left for your prefix.
A fourth habit for anyone inheriting old systems: hunt down the offenders before they fail. Legacy endpoints show up in S3 server access logs and in AWS CloudTrail logs, so a search for requests to s3.amazonaws.com hostnames, or to the dash-style s3-us-west-2 form, lists every client still using an address that only works by courtesy. Fix those while nothing is on fire, and Saturday morning stays quiet.
Ethan has an opinion about all of this, and it's blunt. "Nobody ever got paged because a bucket name was too honest. Put the Region in the name, put the Region in the client, and S3 will never need to redirect you."
Frequently asked questions
What does PermanentRedirect mean in S3?
It's an HTTP 301 telling you the request was sent to the wrong endpoint for that bucket, almost always because the bucket lives in a different Region than the one your client used. The bucket hasn't moved. Buckets can't change Region. Find the real Region and send requests there.
How do I fix "The bucket you are attempting to access must be addressed using the specified endpoint"?
Look up the bucket's Region with aws s3api head-bucket --bucket NAME --query BucketRegion --output text, then pass it: --region in the CLI, the Region on the SDK client, or the Regional hostname in a URL. If it keeps failing, check for a pinned endpoint or a copy command missing --source-region.
How do I find which Region my S3 bucket is in?
Use HeadBucket. In the CLI, head-bucket with --query BucketRegion prints it, and the Region header comes back even without permission on the bucket. In the console, open General purpose buckets and read the Region shown beside the bucket. Skip get-bucket-location; it is no longer a best practice.
Why do I still get PermanentRedirect after setting the region in the AWS CLI?
Something with higher priority is overriding it. The --region flag wins over environment variables, which win over the profile; CLI v2 reads AWS_REGION before AWS_DEFAULT_REGION. A pinned --endpoint-url beats them all. Run aws configure list to see where your Region comes from.
How do I fix PermanentRedirect in boto3 and Python?
Create the client with the bucket's Region: boto3.client("s3", region_name="us-west-2"). If you don't know it in advance, call head_bucket once and read BucketRegion, then build the client from that. Boto3 also reads AWS_DEFAULT_REGION from the environment.
How do I fix "The bucket is in this region, please use this region to retry the request" in Lambda?
The bucket holding your deployment package must be in the same Region as the function. Either deploy the function in the Region the message names, or upload the package to a bucket in the function's own Region. Keeping one artifact bucket per Region prevents a repeat.
Can I move an S3 bucket to a different Region?
No. After creation you can't change a bucket's Region, name or owner. Create a new bucket in the target Region, copy the objects with aws s3 sync --source-region X --region Y, recreate the bucket settings, repoint your apps, and delete the old bucket last.
Does an S3 301 PermanentRedirect error cost money?
No. PermanentRedirect, PermanentRedirectControlError and the 307 TemporaryRedirect are on the list of responses S3 doesn't bill. The successful request that follows is billed normally, and a cross-Region request costs $0.02 per GB for data transfer in the Oregon-to-N. Virginia example, as of September 30, 2026.
How do I make the AWS SDK follow S3 region redirects automatically?
In the Java SDK 2.x, call crossRegionAccessEnabled(true) on the client builder (version 2.20.111 or later). In the JavaScript SDK v3, set followRegionRedirects: true in the S3 client config. Both are off by default, and the first call to a bucket elsewhere can be slower.
Why does aws s3 sync between two Regions give PermanentRedirect?
For bucket-to-bucket commands, --region names the destination, and without --source-region the CLI assumes the source is in the same Region. Pass both: aws s3 sync s3://SRC s3://DST --source-region us-west-2 --region us-east-1.
What is the difference between PermanentRedirect and 307 TemporaryRedirect?
A 301 PermanentRedirect means the address you used is wrong for that bucket and will stay wrong. A 307 TemporaryRedirect appears while DNS catches up, mostly for a bucket created in the last 24 hours, and goes away on its own. Using the Regional endpoint avoids both.
Why does CloudFront return a 307 redirect to my S3 bucket?
A recently created bucket can return 307 for up to 24 hours, because CloudFront forwards to the default endpoint, which is in us-east-1. Change the origin domain to the bucket's Regional form, BUCKET.s3.REGION.amazonaws.com, or use the website endpoint if the bucket is a website.
Is s3.amazonaws.com the global endpoint, and should I still use it?
It's the legacy global endpoint, and it lands in N. Virginia. It works for buckets there and breaks for path-style requests to buckets elsewhere. Use the standard Regional form, s3.REGION.amazonaws.com, for everything.
Why do path-style S3 URLs return a 301 Moved Permanently?
A path-style URL to the legacy global hostname only works for a bucket in N. Virginia. For any other Region, S3 returns a 301 and names the correct URI. Rewrite the link to the Regional, virtual-hosted form: https://BUCKET.s3.REGION.amazonaws.com/key.
Can I find the Region of an S3 bucket in another AWS account?
Yes. HeadBucket returns the Region header for any bucket name in the partition, regardless of permissions on the bucket. That tells you where to send requests, not whether you're allowed in; access is the owner's decision.
I fixed the Region but now I get AccessDenied. What now?
The redirect was hiding a permission problem. Check the bucket policy, your IAM permissions, and any organization policy that limits Regions with aws:RequestedRegion. If the bucket belongs to another account, only the owner can grant access.
Console labels and endpoint rules drift. If your screen disagrees with a step here, go with your screen and tell me which step, and I'll fix the page. And if you're reading this with a blank page in front of customers, breathe. Your data is sitting exactly where you left it, and the fix is one Region name away.
📌 If you keep one line from this page
A bucket never moves, so when S3 says PermanentRedirect, ask it where the bucket lives and send your request there.
One command finds the Region: aws s3api head-bucket --bucket NAME --query BucketRegion --output text.
Revision note. Written September 30, 2026. Two things on this page will age first: the $0.02 per GB cross-Region transfer rate in the cost example, and the two SDK switches, crossRegionAccessEnabled and followRegionRedirects, because client libraries move faster than S3 does. Check those two if a year has passed. The rest, the HeadBucket lookup, the --source-region rule and the 24-hour 307 on a new bucket, has not changed in years.