Fastly CDN (VCL)
DataDome Fastly module detects and protects against bot activity.
This documentation is for version3.x.x
- If you are running with version
2.x.x, please follow this documentation.- If you are upgrading your integration from version
2.x.xto version3.x.x, please follow this guide to ensure a successful migration.
DataDome Bot protection can be integrated directly inside Fastly.
Before the regular Fastly process starts, a preflight request is performed on the closest DataDome endpoint. Depending on the API response, the module either blocks the request or lets Fastly proceed with the regular process.
The module has been implemented to ensure the best user experience: if any errors were to occur during the preflight, or if the timeout is reached, the module will automatically disable its blocking mechanism and allow the regular Fastly process to proceed.
Prerequisites
You need to Contact Fastly support to enable some features to be able to use DataDome Bot Protection
Send an email :
- To: [email protected]
- Subject: Pragmas needed on Service XXXXXXXXXX for DataDome support
In order to enable DataDome support please set these pragmas:
• fix_unsent_body_drain
• no_body_if_bereq_is_get_or_head
on the following service:
• Service XXXXXXXXXX
• Account YYYYYYYYYY
Thanks!
Installation
Before integrating DataDome, you need to ask Fastly Support to enable DataDome support for your Fastly service as described here.
DataDome provides 2 integration options:
- A. [Recommended] VCL snippets through the Fastly dashboard
- B. VCL snippets through Terraform
You can find the code here
A. VCL snippets through the Fastly dashboard
- Create a Fastly service or use an existing one, then create a new version of this service by cloning the actual one.

- Go to VCL snippets

- Download and extract our Fastly module. Fastly snippets are available inside the snippet folder. Upload them one by one :
- Start with the
initone.
Update your own server server key (You can find it inside your dashboard)
- Do the same things from all others vcl snippets :
recv,miss,pass,fetch,erroranddeliver).
For each one, you need to select the corresponding subroutine.
At the end you should have 7 snippets like this. Double check for each one the type is correct.
- Click
activateto deploy the new Fastly configuration.

- You are now protected by DataDome
B. VCL snippets through Terraform
This option provisions the same 7 VCL snippets as option A above, but manages them as code through the Fastly Terraform provider instead of uploading them manually in the dashboard.
- Download and extract our Fastly module. The
snippetfolder contains the 7.vclfiles (init,recv,miss,pass,fetch,error,deliver) that Terraform will upload. - Open the
init.vclfile and set your own server-side key (available in our dashboard), as you would in step 4 of option A. This is also where you can adjust any other setting listed in the Configuration section below. - Create a Fastly API token by following the Fastly documentation, then export it as an environment variable in the shell you will run Terraform from:
export FASTLY_API_KEY=<your API key>- Add the
localsblock and thedynamic "snippet"block below to thefastly_service_vclresource of your existing Fastly service in Terraform. Pointdatadome_snippets_pathto the folder where you extracted the snippets in step 1 (for example, if you copied thesnippetfolder next to your.tffiles,path.modulewill work as-is).
provider "fastly" {}
locals {
# Folder containing the datadome .vcl files extracted in step 1
datadome_snippets_path = path.module
# Name of every DataDome snippet to upload - keep as-is
datadome_snippets = toset([
"init",
"recv",
"pass",
"fetch",
"deliver",
"miss",
"error"
])
}
resource "fastly_service_vcl" "main" {
# name = "datadome_protected_service"
# ... keep the domain, backend and any other block already configured for your service ...
# This dynamic block creates one `snippet` block
# per DataDome snippet listed in local.datadome_snippets
dynamic "snippet" {
for_each = local.datadome_snippets
content {
type = snippet.value
priority = 10
name = format("datadome_%s", snippet.value)
content = file(format("%s/%s.vcl", local.datadome_snippets_path, snippet.value))
}
}
}- Deploy your changes:
terraform init
terraform plan
terraform apply- You are now protected by DataDome.
Configuration
The Fastly module provide two different table to customize the behavior of the module:
datadome_settingsa STRING table that will contain the different settings of the moduledatadome_patternsa REGEX table that will contain every Regular expression used in the module
datadome_settings fields
datadome_settings fields| Setting | Description | Required | Default value |
|---|---|---|---|
server_side_key | Your DataDome server-side key | true | - |
enable_referrer_restoration | Set to true to restore original referrer when a challenge is passed. | false | false |
enable_graphql_support | Set to true to enable GraphQL extraction of operation name on POST request. | false | false |
enable_mcp_support | Set to true to enable the extraction of Model Context Protocol properties. | false | false |
enable_replay_protection | Set to true to prevent replay attack in case of Early-Data requests. | false | false |
remove_matrix_params | Ignore per-segment matrix params from the path to evaluate exclusion regex. | false | true |
datadome_patterns fields
datadome_patterns fields| Setting | Description | Required | Default value |
|---|---|---|---|
static_asset_exclusion | The static -asset extension allowlist. | false | (?i)\.(avi|avif|bmp|css|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|json|less|map|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|otf|png|svg|svgz|swf|ttf|wav|webm|webp|woff|woff2|xml|zip)$ |
url_pattern_exclusion | The user-defined exclusion regex. Leave empty to disable. | false | - |
encoded_char_inclusion | Regex of encoded characters that force a static-asset match back into inspection. Leave empty to disable. | false | (?i)(%23|%25|%2f|%3b|%3f) |
graphql_endpoint | The regex that will be applied to req.url.path to detect GraphQL requests. | false | (?i)graphql |
mcp_endpoint | The regex that will be applied to req.url.path to detect MCP requests. | false | (?i)mcp |
Advanced configuration
GraphQL support
It is possible to enable graphQL support and extract operation type and operation name from the request body.
Set the boolean value of enable_graphql_support to true in the datadome_settings table.
Once enabled, POST requests targeting a GraphQL endpoint with content-type: application/json will be analyzed to extract GraphQL operation name and type.
It is also possible to customize the GraphQL endpoint by updating the graphql_endpoint in the datadome_patterns table.
MCP support
It is possible to enable support for the Model Context Protocol and extract information from the request body.
Set the boolean value of enable_mcp_support to true in the datadome_settings table.
Once enabled, requests targeting an MCP endpoint will be analyzed to extract MCP properties according to the official specification:
mcp-session-idandmcp-protocol-versionheadersjsonRpcVersion,jsonRpcRequestId, andmcpMethodrequest body fieldsmcpParamsClientInfoNameandmcpParamsClientInfoVersionrequest body fields when the method isinitializemcpParamsToolNamerequest body field when the method istools/call
It is also possible to customize the MCP endpoint by updating the mcp_endpoint in the datadome_patterns table.
Referrer restoration
When passing a DataDome challenge on browsers other than Firefox, the referrer value is updated which can lead to inconsistent results in website analytics.
It is possible restore the Referer header to its original value for your backend:
- Contact our support team, they will review your requirements and provide you with the best recommendations.
- Set the boolean value of
enable_referrer_restorationtotruein thedatadome_settingstable
Configure custom data enrichment
Custom data enrichment featureDataDome let you enrich in real time our detection engine by sending us some custom fields with your business data. These fields can be used for specific detection models.
👋 Please reach out to our support team for reviewing the data received.
You can implement a vcl sub-routine called handle_custom_fields to define the values of the custom fields you need.
This subroutine will be used when calling the Bot Protect API, adding the custom values.
Below an example with the custom fields being set dynamically
sub handle_custom_fields {
# sample values, update to your needs
set bereq.http.x-datadome-params:customFieldString1 = req.http.x-custom-header;
set bereq.http.x-datadome-params:customFieldString2 = "customField - String - 2";
set bereq.http.x-datadome-params:customFieldString3 = "customField - String - 3";
set bereq.http.x-datadome-params:customFieldInteger1 = req.http.x-custom-integer;
set bereq.http.x-datadome-params:customFieldInteger2 = "42";
set bereq.http.x-datadome-params:customFieldFloat1 = "3.1415";
# UserID & ProductID
set bereq.http.x-datadome-params:userID = req.http.x-user-id;
set bereq.http.x-datadome-params:productID = req.http.x-product-id;
}FAQ
How to log enriched headers?
Before any setup, please read our requirements about the enriched headers.
- Set up a real-time logging providers
- Edit the log format. DataDome's headers are available in the
req.httpobject.- For example
req.http.x-datadome-isbot
- For example
Please find below an example with Loggly:
{
"timestamp":"%{begin:%Y-%m-%dT%H:%M:%S}t",
"client_ip":"%{req.http.Fastly-Client-IP}V",
"geo_country":"%{client.geo.country_name}V",
"geo_city":"%{client.geo.city}V",
"url":"%{json.escape(req.url)}V",
"request_referer":"%{json.escape(req.http.referer)}V",
"request_user_agent":"%{json.escape(req.http.User-Agent)}V",
"fastly_is_edge":%{if(fastly.ff.visits_this_service == 0, "true", "false")}V,
"response_state":"%{json.escape(fastly_info.state)}V",
"response_status":%{resp.status}V,
"response_reason":%{if(resp.response, "%22"+json.escape(resp.response)+"%22", "null")}V,
"response_body_size":%{resp.body_bytes_written}V,
"request_method":"%{json.escape(req.method)}V",
"request_protocol":"%{json.escape(req.proto)}V",
"fastly_server":"%{json.escape(server.identity)}V",
"host":"%{if(req.http.Fastly-Orig-Host, req.http.Fastly-Orig-Host, req.http.Host)}V",
"datadome-isbot":"%{json.escape(req.http.x-datadome-isbot)}V",
"datadome-botname":"%{json.escape(req.http.x-datadome-botname)}V",
"datadome-ruletype":"%{json.escape(req.http.x-datadome-ruletype)}V",
"datadome-captchapassed":"%{json.escape(req.http.x-datadome-captchapassed)}V"
}How can I avoid POST requests losing their body and failing?
You should ask Fastly Support to enable specific pragmas for your Fastly service as described here.
Does DataDome support Fastly Signal Sciences WAF?
Yes. DataDome is fully compatible with Signal Sciences WAF on Fastly.
DataDome is executed first by design, inside vcl_recv. Fastly Signal Sciences is executed later, just before the request is sent to the origin. This order is fixed by the Fastly integration architecture and cannot be reversed through VCL ordering or configuration. Therefore, Signal Sciences cannot be placed in front of DataDome to reduce the number of requests analyzed by DataDome within the same Fastly service.
How can I skip the detection for specific requests?
It is possible to skip the detection based on the parameters of incoming requests (e.g. User-Agent, URL, ACL list).
To skip DataDome's detection, you need to set the x-datadome-skip-detection header to the value of req.xid variable in a VCL snippet that runs before the vcl_recv subroutine.
# Example of condition to skip DataDome's detection
if (req.url ~ "^/path/to/exclude") {
set req.http.x-datadome-skip-detection = req.xid;
}Are requests in the cache protected by DataDome ?
Yes. DataDome is executed at the beginning of the request (inside vcl_recv) and before the cache lookup.
This ensures that all content is protected, but the DataDome integration does not affect your existing cache configuration or your cache hit ratio.
Updated 2 days ago

