How to Proxy an S3 Static Website

Serguey Arellano Martínez
TribalScale
Published in
5 min readDec 7, 2020

Read Part 1: How To Configure AWS Route 53 here.

I’ve been having a problem with microservices in AWS for a while. How do integrate a swagger UI endpoint within the API itself?

Ideally, I’d have this:

api.mydomain.com/docs
api.mydomain.com/path1
api.mydomain.com/path2
...

There is actually a way I came up with to achieve this that doesn’t need CloudFront:

So why without CloudFront? I don’t want to invalidate the CloudFront instances every time I deploy a new version of my spec and wait until the CDN “unregisters” or “un-caches” the previous content. I want it to be instantaneous.

Creating an S3 static website

Remember to give the bucket the appropriate permissions. You can follow the AWS guide and upload content.

If you want to use the serverless framework I recommend the S3 Sync plugin. It will create the bucket, add the permissions, and stay in sync each time you deploy your service:

Create an API REST

or do so with serverless using CloudFormation syntax:

Create an API structure

The third step consists of creating the structure, resources, and methods for the newly created API:

The root method has to proxy the requests to the URL of your website:

Click on the integration request part to set the endpoint URL:

If you don’t want to do it manually you can do it in serverless in the resource part:

Our problem now is that if you access the API gateway endpoint via <gatewayID>.execute-api.<region>.amazonaws.com/<stage> you will see that your index.html gets downloaded because it was proxied but other resources that the HTML wants to download do not get resolved. It has a problem.

The Problem

If the bucket has the following structure:

index.html
swagge-ui.css

index.html:

<link rel="stylesheet" type="text/css" href="swagger-ui.css" >

using this URL in a browser will retrieve the index.html:

xxxxxxkdu.execute-api.us-east-1.amazonaws.com/dev

and will attempt to retrieve the CSS resource without resolving with the correct stage and cause an error:

xxxxxxkdu.execute-api.us-east-1.amazonaws.com/swagger-ui.css

The Solution

The solution is to adapt the href of your resource to this:

index.html:

<link rel="stylesheet" type="text/css" href="dev/swagger-ui.css" >

Yes, it still won’t work because you didn’t tell your gateway how to resolve that. Also, I’m not happy with using arbitrary dev in the href. What happens when we deploy to prod? In my case I’m using Route 53 to have nice subdomain records like:

dev.mydomain.com/docs
staging.mydomain.com/docs
prod.mydomain.com/docs

So my index.html will look like this:

<link rel="stylesheet" type="text/css" href="docs/swagger-ui.css" >

Firstly, you will need to buy/transfer a domain, get a public certificate (TLS HTTPS), and create some mappings within custom domains in API gateway but that is another topic.

Remember our API structure?

We created a resource called /{item} that will resolve to S3 bucket content and get back with the resource to the browser. There are two things to configure here:

  • permissions for the API gateway to access the bucket contents
  • MIME types to return to the browser with the appropriate headers so it understands what the resource is. Otherwise, you will get resources but the browser won’t know how to treat them.

In method request you need to configure paths and headers:

In integration request you need to configure the headers and path with the following values.

It is important to use path override here, the idea is to map the http path to the bucket resource. My bucket's name is swagger-site-dev

I will return to role permissions later.

For method response we have to make Content-Type and Content-Disposition available.

And map those headers to the response values from S3 in integration response

A way to automate this in serverless with CloudFormation syntax:

Creating a role to give permissions to S3

A way to automate this in serverless would be:

If you are using the console go to IAM -> roles and create a role with permissions

  • AmazonS3ReadOnlyAccess as an AWS managed policy

and trust relationships

After having this role you can add it to integration request -> execution role .

Summary

We created an S3 website and we are accessing it via API gateway, giving us the chance to redirect calls from Route 53 and subdomain records without the need to use CloudFront.

Not using CloudFront means that every time we update the S3 website content we don’t have to invalidate the CDN content first; it gets updated instantly. We are also reducing costs and extra complications of maintaining several CloudFront instances.

To do that we gave our API a specific structure that will:

  • proxy to S3 website when accessing the root path /
  • retrieve specific objects from the bucket when accessing child path /{item}

It looks a little hack-y, but the process is nevertheless logical!

Have any questions about our development capabilities? Click here to chat with one of our experts.

Serguey is a Javascript developer with experience in cloud architectures and a TDD advocate.

TribalScale is a global innovation firm that helps enterprises adapt and thrive in the digital era. We transform teams and processes, build best-in-class digital products, and create disruptive startups. Learn more about us on our website. Connect with us on Twitter, LinkedIn & Facebook!

--

--