Serving Dynamic Web Pages using Python and AWS Lambda

AWS Lambda + Python + Jinja

Step 0 — Optional

If you prefer to develop and test lambda functions locally (as I do), you can use Docker to simulate the AWS lambda function environment. A sample Dockerfile I use is below.

FROM amazonlinux:latest
RUN mkdir -p /mnt/app
ADD . /mnt/app
WORKDIR /mnt/app
RUN yum update -y
RUN yum install gcc -y
RUN yum install gcc-c++ -y
RUN yum install findutils -y
RUN yum install zip -y
RUN amazon-linux-extras install python3=3.6.2
RUN pip3 install --upgrade pip
RUN pip3 install -r requirements.txt -t aws_layer/python
import lambda_function
event = {
"queryStringParameters": {
"param1": "value1"
"path": "/api",
"requestContext": {
"param2": "value2"
res = lambda_function.lambda_handler(event=event, context={})
assert 200 == int(res["statusCode"])

Step 1 — Write html template

In this step, we write the html template the Lambda function will return. A good default is the new Bootstrap 5 CSS framework where the recommended starting markup looks something like this:

Sample HTML page

Step 2 — Write Lambda function to serve your html page

In the example below, the lambda function expects URL parameters and parses those. So when parsing a custom URL, the format would look something like this: See step 10 in this tutorial to add custom URLs to your API Gateway-triggered Lambda functions.

import os
import sys
from jinja2 import Environment, FileSystemLoader
def lambda_handler(event, context):
env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates"), encoding="utf8"))
my_name = False
if event["queryStringParameters"] and "my_name" in event["queryStringParameters"]:
my_name_query = event["queryStringParameters"]["my_name"]
template = env.get_template("index.html")
html = template.render(
return response(html)
def response(myhtml):
return {
"statusCode": 200,
"body": myhtml,
"headers": {
"Content-Type": "text/html",
  • jinja2 loads your previously created index.html using class “FileSystemLoader” and we store it as variable “env”
  • variable “my_name” is parsed from the URL query parameters as explained above and stored as the Python variable my_name_query
  • the jinja2 render function then passes my_name_query to the template and returns the html page



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store