How to Fix Amazon CloudFront Root Domain Redirect Issue with S3 Website Endpoints


2 views

When setting up domain redirects using Amazon S3 and CloudFront, everything works perfectly except for the root domain (e.g., www.example.com). Instead of redirecting, CloudFront returns an XML file describing the S3 bucket:

<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>example-com</Name>
<Prefix></Prefix>
<Marker></Marker>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
</ListBucketResult>

CloudFront expects a Default Root Object when accessing the root path. However, S3's redirect bucket doesn't have a physical file to serve, so CloudFront falls back to listing the bucket contents.

Since S3 website endpoints alone can't solve this, we'll use Lambda@Edge to intercept the root request and force a redirect:

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    
    // Check if this is a root request
    if (request.uri === '/') {
        return {
            status: '301',
            statusDescription: 'Moved Permanently',
            headers: {
                'location': [{
                    key: 'Location',
                    value: 'https://example.com'
                }]
            }
        };
    }
    
    // For non-root requests, proceed normally
    return request;
};
  1. Create the Lambda function in US-East-1 (N. Virginia)
  2. Deploy it to CloudFront as a Viewer Request trigger
  3. Configure your CloudFront distribution:
Origin Domain: example-com.s3-website-us-west-1.amazonaws.com
Origin Path: (leave blank)
Default Root Object: (leave blank)

After deployment (which can take 5-10 minutes), test with:

curl -I https://www.example.com
# Should return 301 with Location: https://example.com

For those who prefer not to use Lambda:

  1. Create an empty index.html in your S3 bucket
  2. Configure CloudFront to return 404 for root requests
  3. Set up a custom error response that redirects:
Error Code: 404
Response Page Path: /redirect.html
HTTP Response Code: 301

Then create redirect.html with meta refresh or JavaScript redirect.

The Lambda@Edge solution adds about 50-100ms latency to root requests, but subsequent requests are cached at edge locations. The S3 custom error approach has no added latency but requires an extra hop.


When configuring Amazon CloudFront with S3 for domain redirects, a common pitfall occurs with root domain requests (e.g., www.example.com). While path-specific redirects work perfectly (e.g., www.example.com/path), accessing the bare domain returns either:

  • An XML bucket listing (ListBucketResult)
  • A failed redirect to /index.html

The root cause stems from how CloudFront interacts with S3 website endpoints versus REST endpoints:

// Incorrect origin setup (REST endpoint)
Origin Domain: my-bucket.s3.amazonaws.com

// Correct origin setup (Website endpoint)
Origin Domain: my-bucket.s3-website-us-east-1.amazonaws.com

Here's the complete workflow to implement proper root domain redirects:

S3 Bucket Configuration

1. Create a dedicated redirect bucket
2. Enable "Static website hosting"
3. Set redirect rule:

{
  "RedirectAllRequestsTo": {
    "HostName": "example.com",
    "Protocol": "https"
  }
}

CloudFront Settings

Critical configuration parameters:

Origin Domain: my-redirect-bucket.s3-website-[region].amazonaws.com
Origin Path: [leave blank]
Protocol: HTTP only (S3 website endpoints don't support HTTPS)
Default Root Object: [leave blank]

For advanced control, implement a Lambda@Edge function:

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  const host = request.headers['host'][0].value;
  
  return {
    status: '301',
    statusDescription: 'Moved Permanently',
    headers: {
      'location': [{
        key: 'Location',
        value: https://example.com${request.uri}
      }]
    }
  };
};

Verify your setup with these curl commands:

# Should return 301 redirect
curl -I http://www.example.com

# Should show Location header
curl -v http://subdomain.example.com

# Verify no XML output
curl http://redirect-domain.com | grep -v "ListBucketResult"

The S3+CloudFront solution provides:

  • ~100ms redirect latency (vs 300-500ms for server-based)
  • 99.9% availability SLA
  • No cold-start delays (unlike Lambda solutions)

Remember to invalidate your CloudFront cache (/ path) after configuration changes.