Optimizely Web Experimentation self-hosting for Amazon Cloudfront users

  • Updated
This topic describes how to:
  • Set up CDN self-hosting using Amazon Cloudfront

If you need to download and self-host the Optimizely Web Experimentation snippet on your own site, Amazon Cloudfront offers an easy way to do it via multiple AWS services working together. And because this approach involves setting up a webhook in Optimizely Web Experimentation, there will be no need for you to poll for any revisions in your snippet.

Self-hosting is ideal for customers who are using HTTP/2 to serve their website and Amazon Cloudfront as their 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 begin its download earlier than if it was being downloaded directly from Optimizely Web Experimentation.

This article will describe how to set up a Lambda function to listen for changes in your Optimizely Web Experimentation snippet, how to save that snippet to your S3 bucket, how to call that script from your Optimizely Web Experimentation, and how to set up your webhook.

This article will help you sync the Optimizely Web Experimentation snippet into an S3 bucket, and it, therefore, assumes you are using Cloudfront with an S3 origin.

Create a Lambda function

The first step in the process is to create a new Lambda function. This function will listen for the Optimizely Web Experimentation webhook and update your local version of the snippet.

From the AWS interface, create a new Lambda function, starting with a blueprint. microservice-http-endpoint

When you create the Lambda function, you can opt to use either NodeJS or Python3.
  1. Give your new function a descriptive name, such as optimizelywebhook

  2. Create a new IAM role, or use an existing IAM role for this function that has access to both:

  • Amazon S3 (policy: AmazonS3FullAccess), and

  • Amazon CloudWatch logs (policy: AWSLambdaBasicExecutionRole or CloudWatchLogsFullAccess)

  1. Next, create the function. This will enable you to edit your code.

  2. Replace YOUR-AWS-S3-BUCKET-NAME with the bucket where you'd like to store your copy of your Optimizely Web Experimentation snippet.

Below is an example of an appropriate Lambda function:

import boto3
import json
from botocore.vendored import requests

def resp(err = None, res = {}):
   resp_body = {'message': str(err)} if err else res
   return {
       'statusCode': '400' if err else '200',
       'body': json.dumps(resp_body),
       'headers': {
           'Content-Type': 'application/json',

def Lambda_handler(event, context):
   err = None
   res = {}
   print('Received webhook: {}'.format(event['headers']['Host']))
   if event['httpMethod'] != 'POST':
       return resp(ValueError('Must be a post request'))
   # fetch snippet from Optimizely CDN
       snippet_metadata = json.loads(event['body'])
       snippet_origin   = snippet_metadata.get('data', {}).get('origin_url')
       project_id       = snippet_metadata.get('project_id')
       snippet_updated  = snippet_metadata.get('timestamp')
       snippet_contents = requests.get(snippet_origin).text
   except Exception as e:
       err = e
   # write snippet content to s3
       s3       = boto3.resource('s3')
       s3object = s3.Object('YOUR-AWS-S3-BUCKET-NAME', 'snippet/{}.js'.format(project_id))
       s3object.put(Body=snippet_contents, ACL='public-read', ContentType='application/javascript')
   except Exception as e:
       err = e
   return resp(err, {'message': 'success'})

This example configures the function to only accept POST requests, parse that request (based on a successful POST request), and save the Optimizely Web Experimentation snippet to your AWS S3 Bucket. The snippet is saved as your-aws-s3-bucket-name.com/snippet/PROJECTID.js

  1. After saving your edits to the function, navigate to your API gateway trigger.

  2. Under the API Gateway section, take note of your API endpoint. You will need this later for setting up your Optimizely Web Experimentation webhook.

Configure the Optimizely Web Experimentation webhook

  1. In Optimizely Web Experimentation, open the project you would like to self-host and navigate to Settings.

  2. Navigate to the Webhooks tab and click Create New Webhook…

  3. You will be prompted for a URL where a POST request can be sent each time your snippet is updated. Enter the URL of your Lambda API endpoint from step 6 above.

Configure a custom TTL (optional)

To configure a custom TTL for this script, you will need to change your cache setting in your Cloudfront distribution from Customize to Use Origin Cache Headers.

You will then be able to include a cache-control setting in the function that writes the Optimizely Web Experimentation data to S3.


s3object.put(Body=snippet_contents, ACL='public-read', 
ContentType='application/javascript', CacheControl='max-age=120')

If you want your base Cloudfront TTL settings to apply to this script, leave Object Caching set to Customize.

Modify the Optimizely Web Experimentation snippet

If the Optimizely Web Experimentation snippet is already installed on your page, you will need to remove it and replace it with the new script tag that references your self-hosting path.

If you are new to Optimizely Web Experimentation, add this new script tag in the appropriate spot inside the <head> tag on your page.

The format of this snippet should be:

<script src=”your-aws-s3-bucket-name.com/snippet/PROJECTID.js”></script>


<script src="https://cdn.optimizely.com/js/123456789.js"></script>

should now be:

<script src=”your-aws-s3-bucket-name.com/snippet/123456789.js”></script>

Repeat this step for any additional snippets you may have across your site.

Do not forget the /snippet/PROJECTID.js portion is from the Lambda code and should reflect what is in your Lambda function.