Reverse Proxy using AWS Lambda

Reverse Proxy using AWS Lambda

A reverse proxy is a server that acts as an intermediary between client devices (such as web browsers) and backend servers. Unlike a traditional forward proxy, which sits between client devices and the internet to facilitate requests to external servers, a reverse proxy handles requests on behalf of servers.

AWS Lambda is a serverless computing service provided by Amazon Web Services (AWS). It allows you to run code without provisioning or managing servers, offering a pay-as-you-go model based on the actual compute time your code consumes. AWS Lambda is designed to enable developers to focus on writing code and building applications without the need to worry about infrastructure management.

AWS Lambda also offers a dedicated HTTP(S) endpoint for your Lambda function.

This allows you to create an on-demand reverse proxy pretty easily that won’t cost a lot since AWS offers a generous free tier.

The full source code is available here – https://github.com/jobinbasani/aws-lambda-reverse-proxy

You can easily create AWS resources using the AWS Cloud Development Kit (CDK) which supports different programming languages including JavaScript, Python, Go, C# and Java.

Go includes a built-in reverse proxy in the httputil package.

You can modify the request and response very easily, or just don’t modify them.

To simplify things, let’s also use a wrapper library that provides a Go net/http compatibility layer. I’m using the one at github.com/morelj/lambada

Our Lambda function looks like this:

func main() {
    lambada.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        baseUrl := os.Getenv("TARGET_BASE_URL")

        if len(baseUrl) == 0 {
            writeError(w, "TARGET_BASE_URL environment variable is not set")
            return
        }

        target, err := url.Parse(baseUrl)
        if err != nil {
            writeError(w, fmt.Sprintf("Error parsing base url: %s", err.Error()))
            return
        }
        proxy := &httputil.ReverseProxy{
            Director: func(req *http.Request) {
                path := req.URL.Path
                if !strings.HasPrefix(path, "/") && len(path) > 0 {
                    path = "/" + path
                }
                req.URL.Scheme = target.Scheme
                req.URL.Host = target.Host
                req.Host = target.Host
                req.URL.Path = path
                req.Header.Set("X-Forwarded-Host", req.Host)
            },
        }
        proxy.ServeHTTP(w, r)
    }))
}

Any incoming request will be proxied to the URL defined using the environment variable TARGET_BASE_URL.

Let’s create our resources using CDK now.

If you haven’t installed CDK, run the below command.

npm install -g aws-cdk

And now, let’s create a project that uses Go

cdk init app --language go

Add the awscdklambdagoalpha dependency to the generated project

go get github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2

Now let’s create a Lambda function

goLambdaFunction := awscdklambdagoalpha.NewGoFunction(
		stack,
		jsii.String("reverse-proxy"),
		&awscdklambdagoalpha.GoFunctionProps{
			Entry: jsii.String("../lambda/"),
			Environment: &map[string]*string{
				"TARGET_BASE_URL": jsii.String("https://gobyexample.com"),
			},
			Bundling: &awscdklambdagoalpha.BundlingOptions{
				Environment: &map[string]*string{
					"CGO_ENABLED": jsii.String("0"),
				},
			},
		})

Let’s also create a function URL that allows us to access the function via HTTP

functionUrl := goLambdaFunction.AddFunctionUrl(&awslambda.FunctionUrlOptions{
		AuthType: awslambda.FunctionUrlAuthType_NONE,
		Cors: &awslambda.FunctionUrlCorsOptions{
			AllowedOrigins: &[]*string{jsii.String("*")},
		},
	})

Let’s add the URL to the output of the Cloudformation stack created by CDK

awscdk.NewCfnOutput(stack, jsii.String("reverse-proxy-url"), &awscdk.CfnOutputProps{
		Value:       functionUrl.Url(),
		Description: jsii.String("Reverse proxy URL"),
	})

Let’s publish the resources!

cdk deploy

If everything was successful, you will see an output similar to the one below:

InfraStack: deploying... [1/1]
InfraStack: creating CloudFormation changeset...

 ✅  InfraStack

✨  Deployment time: 25.73s

Outputs:
InfraStack.reverseproxyurl = https://xxxxxxxxx.lambda-url.us-west-2.on.aws/
Stack ARN:
arn:aws:cloudformation:us-west-2:xxxxxxx:stack/InfraStack/07df5180-a039-11ee-a24a-02ac0fef9d23

✨  Total time: 30.6s

Access the InfraStack.reverseproxyurl from the outputs and see how its proxies the website. Change the TARGET_BASE_URL environment variable and deploy again to test with another website.

Source code – https://github.com/jobinbasani/aws-lambda-reverse-proxy

Leave a Reply

Your email address will not be published. Required fields are marked *