Can I use AWS API Gateway as a reverse proxy for a S3 website?

I have a serverless website on AWS S3. But S3 have a limitation that I want to overcome: it don't allow me to have friendly URLs.

For example, I would like to replace URL:

www.mywebsite.com/user.html?login=daniel

With this URL friendly:

www.mywebsite.com/user/daniel

So, I would like to know if I can use Lambda together with API Gateway to achieve this.

My idea is:

API Gateway ---> Lambda function ---> fetch S3 resource

The API Gateway will get ANY request, and pass information to a Lambda funcion, that will process some logic using the request URL (including maybe some database query) and then fetch the resource from S3.

I know AWS API Gateway main purpose is to be a gateway to REST APIs, but can we also use it as a proxy to an entire website?

Answers 1

  • It is possible to use API Gateway as a reverse proxy for a S3 website.

    I was able to do that following steps below:

    • In AWS API Gateway, create a "proxy resource" with resource path = "{proxy+}"
    • Go to AWS Certificate Manager and request a wildcard certificate for your website (*.mywebsite.com)
    • AWS will tell you to create a CNAME record in you domain registrar, to verify that you own that domain
    • After your certificate is validated, go to AWS API Gateway and create a Custom Domain Name (click on "Custom Domain Names" and then "Create Custom Domain Name"). In "domain name" type your domain (www.mywebsite.com) and select the ACM Certificate that you just created (step 1 above). Create a "Base Path Mapping" with path = "/" and in "destination" select your API and stage.
    • After that, you will need to add another CNAME record, with the CloudFront "Target Domain Name" that was generated for that Custom Domain Name.

    In the Lambda, we can route the requests:

    'use strict';
    
    const AWS = require('aws-sdk');
    const s3 = new AWS.S3();
    const myBucket = 'myBucket';
    
    exports.handler = async (event) => {
    
        var responseBody = "";
    
        if (event.path=="/") {
            responseBody = "<h1>My Landing Page</h1>";
            responseBody += "<a href='/xpto'>link to another page</a>";
            return buildResponse(200, responseBody);
        }
    
        if (event.path=="/xpto") {
            responseBody = "<h1>Another Page</h1>";
            responseBody += "<a href='/'>home</a>";
            return buildResponse(200, responseBody);
        }
    
        if (event.path=="/my-s3-resource") {
    
            var params = { 
                Bucket: myBucket,
                Key: 'path/to/my-s3-resource.html',
            };
    
            const data = await s3.getObject(params).promise();
    
            return buildResponse(200, data.Body.toString('utf-8'));
        }
    
        return buildResponse(404, '404 Error');
    
    };
    
    
    function buildResponse(statusCode, responseBody) {
    
        var response = {
            "isBase64Encoded": false,
            "statusCode": statusCode,
            "headers": {
                "Content-Type" : "text/html; charset=utf-8"
            },
            "body": responseBody,
        };
    
        return response;
    }
    

Related Articles