Using lambda@Edge for Server-Side Rendering - MEDWING Engineering, Product & Design

By Mohamed Elfiky

At MEDWING we recently launched 2 new initiatives - wirwollenhelfen and wirbrauchenhilfe - as our response to the Corona-Crisis.

Both of these initiatives had a web presence - a few static pages and a form. These pages were built using react and hosted on S3 and Cloudfront. However, we needed to add Server-Side Rendering to improve SEO performance.

We first thought of using Next.js as it is a popular framework. However, it is a big framework with a lot of dependencies, which is a significant overhead for the simplicity of the project. Also, React.js already supports SSR by default.

So we decided not to go with the Next.js framework. Additionally, we wanted to test Lambda@Edge in this project, for better scalability and for avoiding server provisioning, enabling auto-scaling, simplifying maintenance, and reducing cost.

We used terraform for automating infrastructure creation at the beginning of the project because it helps us to track the infrastructure we use for each project in AWS and store the infrastructure changes in our version control. So we continued using terraform for implementing the serverless architecture.

Thanks to AWS Lambda Edge, we were able to create a lambda function associated with each CloudFront Edge that hosts the static content to handle the SSR.

But how?

Lambda@Edge lets you run Node.js and Python Lambda functions to customize content that CloudFront delivers, executing the functions in AWS locations closer to the user. The functions run in response to CloudFront events. You can use Lambda functions to change CloudFront requests and responses at the following points:

  • After CloudFront receives a request from a viewer (viewer request)
  • Before CloudFront forwards the request to the origin (origin request)
  • After CloudFront receives the response from the origin (origin response)
  • Before CloudFront forwards the response to the viewer (viewer response)
Lambda Edge diagram
Lambda Edge diagram

Each event has specific use cases, and you can check the AWS docs for a better understanding of the use cases and limitations.

For our use case, the origin response event fits better because the website has static content, and it’s fine to cache the content for a while and save the lambda calls time/cost, and also for better loading performance.

So we started using Lambda edge in server-side rendering. But we needed to handle some use cases.

  • Rendering static assets like images, SVG, JS, CSS directly from S3 without calling lambda.
  • Configuring Lambda@edge CloudFront request structure to be compatible with Koa HTTP Library.
  • Optimizing WebPack server-build to use the existing client-build instead of building the client code again with the server build.

Rendering static assets:

Although we configured the koa router to serve static assets, we need to make sure that all assets requests go directly to S3, because:

  • Server-side rendering is useless in static asset requests.
  • S3 should control the caching headers for assets, which are being set by our CI in the deployment step.

Fortunately, CloudFront gives us the ability to configure the cache behaviors based on path patterns.

So we used this feature to differentiate between a page request that requires SSR and an asset request that goes directly to S3.

For example:

CloudFront Distribution Terraform Config

In the previous snippet, we configured all requests with the pattern to go to S3 directly, and all other requests go to lambda for server-side rendering.

Side Note:

It’ s recommended to enable CloudFront compression for better performance because it decreases asset size significantly, which leads to better page speed.