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