Self-host with Amazon CloudFront
How to self-host with Amazon CloudFront for Optimizely Web Experimentation
Self-hosting is ideal for customers who are using both HTTP/2 to serve their website and a CDN. By self-hosting, you can eliminate an SSL connection to Optimizely Web Experimentation while using multiplexing to request the snippet faster.
While self-hosting with an HTTP/1 connection may eliminate an additional DNS lookup and the SSL handshake, there is no guarantee the script will download earlier than if it was downloaded directly from Optimizely.
You can use AWS Lambda, S3, and CloudFront to automatically synchronize your snippet updates.
Prerequisites
- An active Optimizely Web Experimentation project.
- An AWS account with permissions to create Lambda functions, S3 buckets, and API Gateway resources.
- A CloudFront distribution configured with an S3 origin pointing to your designated bucket.
Steps
Create an S3 bucket
If you do not already have one, create an S3 bucket to store your Optimizely snippet files. This bucket is the origin for your CloudFront distribution. Ensure your bucket policy has public read access to the files within the snippet/ prefix.
Create a Lambda function
This Lambda function handles webhook requests from Optimizely, fetches the updated snippet, and stores it in your S3 bucket.
- Go to Lambda in the AWS console and create a function. Choose Author from scratch.
- Give your function a descriptive name, such as optimizely-snippet-updater.
- Choose a recent, stable Python runtime supported by AWS Lambda. Check the AWS Lambda documentation for the latest recommended version.
- Create a IAM role or use an existing one that grants the Lambda function permissions to Amazon S3 and CloudWatch Logs.
- Amazon S3 – AmazonS3FullAccess or a custom policy with permissions to write objects to your specific bucket.
- CloudWatch Logs – AWSLambdaBasicExecutionRole or CloudWatchLogsFullAccess
- Add an API Gateway trigger to your Lambda function. This creates a REST API endpoint that Optimizely can use to send webhook notifications. Choose Create a new API and REST API.
Prepare the Lambda function code
Replace the default Lambda function code with the following Python code. This code uses the requests library to fetch the snippet and the boto3 library to interact with S3.
import boto3
import json
import requests
import os
s3 = boto3.resource('s3')
BUCKET_NAME = os.environ.get('S3_BUCKET_NAME')
def lambda_handler(event, context):
try:
print(f"Received webhook from: {event.get('headers', {}).get('Host', 'Unknown Host')}")
if event['httpMethod'] != 'POST':
return {
'statusCode': 400,
'body': json.dumps({'message': 'Must be a POST request'}),
'headers': {'Content-Type': 'application/json'}
}
snippet_metadata = json.loads(event['body'])
snippet_origin = snippet_metadata.get('data', {}).get('origin_url')
project_id = snippet_metadata.get('project_id')
if not snippet_origin or not project_id:
return {
'statusCode': 400,
'body': json.dumps({'message': 'Missing snippet origin or project ID'}),
'headers': {'Content-Type': 'application/json'}
}
snippet_contents = requests.get(snippet_origin).text
s3_object = s3.Object(BUCKET_NAME, f'snippet/{project_id}.js')
s3_object.put(Body=snippet_contents, ACL='public-read', ContentType='application/javascript') #, CacheControl='max-age=120') # Uncomment for custom cache control
return {
'statusCode': 200,
'body': json.dumps({'message': 'Snippet updated successfully'}),
'headers': {'Content-Type': 'application/json'}
}
except Exception as e:
print(f"Error: {e}")
return {
'statusCode': 500,
'body': json.dumps({'message': f'Error updating snippet: {e}'}),
'headers': {'Content-Type': 'application/json'}
}
Add request library to Lambda function
- Create a file named
requirements.txt
in the same directory as your Lambda function code. - Add the following line:
requests
. - Create a deployment package containing your Lambda function code and the
requirements.txt
file. You can use a zip file or a tool like AWS SAM or Serverless Framework. - Upload the deployment package to your Lambda function. Lambda automatically installs the requests library.
Configure environment variables
In your Lambda function's configuration, go to Configuration > Environment variables and add a variable named S3_BUCKET_NAME
with the value of your S3 bucket name.
Configure Optimizely webhook
- Go to Settings > Webhooks > Create new webhook in Optimizely Web Experimentation.
- Enter the URL of your Lambda API endpoint (obtained from the API Gateway trigger).
- Configure the webhook to trigger on snippet updates.
Include the self-hosted snippet
- Remove any existing Optimizely snippet from your website.
- Add the following
<script>
tag within the<head>
section of your HTML, replacingyour-aws-s3-bucket-name.com
with your S3 bucket's domain or CloudFront distribution domain andPROJECTID
with your actual Optimizely project ID:<script src="https://your-aws-s3-bucket-name.com/snippet/PROJECTID.js"></script>
Configure CloudFront cache control (Optional)
If you want to customize the caching behavior of your snippet on CloudFront, you can set the CacheControl header in the Lambda function code (uncomment the line). However, CloudFront's default caching behavior or any custom caching rules you have configured in your CloudFront distribution apply even if you do not set this header.
Troubleshoot
- Monitor CloudWatch logs – Monitor the CloudWatch logs for your Lambda function to identify and resolve any errors.
- Verify snippet delivery – Use your browser's developer tools to confirm that the self-hosted snippet is being loaded correctly from CloudFront.
Updated about 11 hours ago