Serving Default index.html Files with AWS S3 and CloudFront

AWS S3 combined with CloudFront is a great way to serve a static website, but I found a gotcha that was almost a deal killer for me.

AWS S3 combined with CloudFront is a great way to serve a static website, but I found a gotcha that was almost a deal killer for me. When creating a CloudFront distribution, you have to choose between using the S3 website endpoint (e.g., example.com.s3.amazonaws.com) or the S3 REST endpoint (e.g., example.com.s3-website-us-east-1.amazonaws.com) as the origin for requests. There are pros and cons for each. See here for a full comparison. The big gotcha with choosing a REST endpoint is that URLs such as https://example.com/posts/cool-post will return a 403 HTTP status code and serve an “access denied” XML document if the file you are trying to reach is https://example.com/posts/cool-post/index.html. CloudFront will not serve a default index page when using an S3 REST origin.

CloudFront does let you configure a “default root object”, so if you set it to index.html, when you request https://example.com it will server https://example.com/index.html. However, this setting does not affect anything except the root path.

Why is this a problem? If you want to serve a static website and use simple URLs using an S3 REST origin, you are out of luck (unless you use the Lambda@Edge solution below). You have to specify the whole path including index.html in all links, and worse yet, your users have to type in the whole path including index.html if they are entering a link manually.

What’s the solution?

The easiest is to use an S3 website endpoint for your CloudFront origin instead of the REST endpoint. There are some downsides to that though. For example, with an S3 website endpoint, the request from CloudFront to S3 must be HTTP not HTTPS. Also, you cannot force your users to only access your content through CloudFront when using an S3 website endpoint because you have to allow public access to the S3 endpoint. For many applications that may be OK though.

Another solution that adds a little complexity, but may give you the best of both worlds is to use AWS Lambda@Edge with CloudFront and S3 to serve default index pages. Read this and this for more details on that solution.

Further reading: