How to Proxy an S3 Static Website
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!