Skip to content
Docs

Firewall Terraform Configuration

Learn how to create scalable firewall configurations with Terraform

DX Team

In this guide, you will learn how to apply a series of firewall rules to multiple projects or teams by using Terraform to create configurations that are straightforward to manage and scalable.

Copy link to headingPrerequisites

Copy link to headingAuthenticate Terraform with Edgenivo

Create a folder for your terraform setup

mkdir terraform-firewall
cd terraform-firewall

Create a .env file or export your token:

export VERCEL_TOKEN="your-vercel-token"

Then, create a Terraform file in your project folder and add Edgenivo as a provider.

Create a file called main.tf and paste the following content:

terraform {
required_providers {
vercel = {
source = "vercel/vercel"
version = "~> 0.3"
}
}
}

Run the following in the command line to initialize the project using main.tf:

terraform init

Copy link to headingImport your existing projects

If your Edgenivo projects already exist, import them into Terraform state:

terraform import vercel_project.PROJECT_1 prj_abc123
terraform import vercel_project.PROJECT_2 prj_def456

Where prj_abc123 and prj_def456 are the Edgenivo project IDs.

This links your Terraform code to existing Edgenivo projects.

Copy link to headingDefine your firewall rules as variables

Your configuration will include the following rules:

  • Suspicious user agents — Blocks known bots/crawlers.
  • AI bots — Detects requests from AI services like GPTBot, Claude, etc.
  • OFAC countries — Blocks traffic from sanctioned regions.
  • WordPress URLs — Blocks scanners looking for common WordPress endpoints.
  • Rate limiting — Limits API request frequency (per IP or JA4 fingerprint).

Define them in a vercel_firewall.tf file as follows:

  • Suspicious user agents
    variable "suspicious_user_agent_conditions" {
    description = "Conditions to challenge requests with suspicious user agents."
    type = list(object({
    type = string
    op = string
    value = string
    }))
    default = [
    {
    type = "user_agent"
    op = "ex"
    value = "01h4x.com|360Spider|404checker|404enemy|80legs|ADmantX|AIBOT|ALittle Client|ASPSeek|Abonti|Aboundex|Aboundexbot|Acunetix|AdsTxtCrawlerTP|AfD-Verbotsverfahren|AhrefsBot|AiHitBot|Aipbot|Alexibot|AllSubmitter|Alligator|AlphaBot|Anarchie|Anarchy|Anarchy99|Ankit|Anthill|Apexoo|Aspiegel|Asterias|Atomseobot|Attach|AwarioBot|AwarioRssBot|AwarioSmartBot|BBBike|BDCbot|BDFetch|BLEXBot|BackDoorBot|BackStreet|BackWeb|Backlink-Ceck|BacklinkCrawler|BacklinksExtendedBot|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BetaBot|Bigfoot|Bitacle|BlackWidow|Black Hole|Blackboard|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Buck|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Bytespider|CATExplorador|CCBot|CODE87|CSHttp|Calculon|CazoodleBot|Cegbfeieh|CensysInspect|ChatGPT-User|CheTeam|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Citoid|Claritybot|ClaudeBot|Cliqzbot|Cloud mapping|Cocolyzebot|Cogentbot|Collector|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|Crawling at Home Project|CrazyWebCrawler|Crescent|CrunchBot|Curious|Custo|CyotekWebCopy|DBLBot|DIIbot|DSearch|DTS Agent|DataCha0s|DatabaseDriverMysqli|Demon|Deusu|Devil|Digincore|DigitalPebble|Dirbuster|Disco|Discobot|Discoverybot|Dispatch|DittoSpyder|DnBCrawler-Analytics|DnyzBot|DomCopBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Domains Project|Dotbot|Download Wonder|Dragonfly|Drip|ECCP/1.0|EMail Siphon|EMail Wolf|EasyDL|Ebingbong|Ecxi|EirGrabber|EroCrawler|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|EyeNetIE|Ezooms|FDM|FHscan|FacebookBot|FemtosearchBot|Fimap|Firefox/7.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fuzz|FyberSpider|Fyrebot|G-i-g-a-b-o-t|GPTBot|GT::WWW|GalaxyBot|Genieo|GermCrawler|GetRight|GetWeb|Getintent|Gigabot|Go!Zilla|Go-Ahead-Got-It|GoZilla|Gotit|GrabNet|Grabber|Grafula|GrapeFX|GrapeshotCrawler|GridBot|HEADMasterSEO|HMView|HTMLparser|HTTP::Lite|HTTrack|Haansoft|HaosouSpider|Harvest|Havij|Heritrix|Hloader|HonoluluBot|Humanlinks|HybridBot|IDBTE4M|IDBot|IRLbot|Iblog|Id-search|IlseBot|Image Fetch|Image Sucker|ImagesiftBot|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|Information Security Team InfraSec Scanner|InfraSec Scanner|Intelliseek|InterGET|InternetMeasurement|InternetSeer|Internet Ninja|Iria|Iskanie|IstellaBot|JOC Web Spider|JamesBOT|Jbrofuzz|JennyBot|JetCar|Jetty|JikeSpider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keybot Translation-Search-Machine|Keyword Density|Kinza|Kozmosbot|LNSpiderguy|LWP::Simple|Lanshanbot|Larbin|Leap|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|LieBaoFast|Lightspeedsystems|Likse|LinkScan|LinkWalker|Linkbot|LinkextractorPro|LinkpadBot|LinksManager|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Lipperhey Spider|Litemage_walker|Lmspider|Ltx71|MFC_Tear_Sample|MIDown tool|MIIxpc|MJ12bot|MQQBrowser|MSFrontPage|MSIECrawler|MTRobot|Mag-Net|Magnet|Mail.RU_Bot|Majestic-SEO|Majestic12|Majestic SEO|MarkMonitor|MarkWatch|Mass Downloader|Masscan|Mata Hari|MauiBot|Mb2345Browser|MeanPath Bot|Meanpathbot|Mediatoolkitbot|MegaIndex.ru|Metauri|MicroMessenger|Microsoft Data Access|Microsoft URL Control|Minefield|Mister PiX|Moblie Safari|Mojeek|Mojolicious|MolokaiBot|Morfeus Fucking Scanner|Mozlila|Mr.4x3|Msrabot|Musobot|NICErsPRO|NPbot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|NetLyzer|NetMechanic|NetSpider|NetZIP|Net Vampire|Netcraft|Nettrack|Netvibes|NextGenSearchBot|Nibbler|Niki-bot|Nikto|NimbleCrawler|Nimbostratus|Ninja|Nmap|Nuclei|Nutch|Octopus|Offline Explorer|Offline Navigator|OnCrawl|OpenLinkProfiler|OpenVAS|Openfind|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PECL::HTTP|PHPCrawl|POE-Component-Client-HTTP|PageAnalyzer|PageGrabber|PageScorer|PageThing.com|Page Analyzer|Pandalytics|Panscient|Papa Foto|Pavuk|PeoplePal|Petalbot|Pi-Monster|Picscout|Picsearch|PictureFinder|Piepmatz|Pimonster|Pixray|PleaseCrawl|Pockey|ProPowerBot|ProWebWalker|Probethenet|Proximic|Psbot|Pu_iN|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RSSingBot|Rainbot|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|Re-re|ReGet|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SBIder|SEOkicks|SEOkicks-Robot|SEOlyt|SEOlyticsCrawler|SEOprofiler|SEOstats|SISTRIX|SMTBot|SalesIntelligent|ScanAlert|Scanbot|ScoutJet|Scrapy|Screaming|ScreenerBot|ScrepyBot|Searchestate|SearchmetricsBot|Seekport|SeekportBot|SemanticJuice|Semrush|SemrushBot|SentiBot|SenutoBot|SeoSiteCheckup|SeobilityBot|Seomoz|Shodan|Siphon|SiteCheckerBotCrawler|SiteExplorer|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitebeam|Siteimprove|Sitevigil|SlySearch|SmartDownload|Snake|Snapbot|Snoopy|SocialRankIOBot|Sociscraper|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spider_Bot|Spider_Bot/3.0|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|Szukacz|T0PHackTeam|T8Abot|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|TheNomad|The Intraformant|Thumbor|TightTwatBot|TinyTestBot|Titan|Toata|Toweyabot|Tracemyfile|Trendiction|Trendictionbot|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|URLy.Warning|URLy Warning|UnisterBot|Upflow|V-BOT|VB Project|VCI|Vacuum|Vagabondo|VelenPublicWebCrawler|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|WASALive-Bot|WBSearchBot|WEBDAV|WISENutbot|WPScan|WWW-Collector-E|WWW-Mechanize|WWW::Mechanize|WWWOFFLE|Wallpapers|Wallpapers/3.0|WallpapersHD|WeSEE|WebAuto|WebBandit|WebCollage|WebCopier|WebEnhancer|WebFetch|WebFuck|WebGo IS|WebImageCollector|WebLeacher|WebPix|WebReaper|WebSauger|WebStripper|WebSucker|WebWhacker|WebZIP|Web Auto|Web Collage|Web Enhancer|Web Fetch|Web Fuck|Web Pix|Web Sauger|Web Sucker|Webalta|WebmasterWorldForumBot|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|Whack|Whacker|Whatweb|Who.is Bot|Widow|WinHTTrack|WiseGuys Robot|Wonderbot|Woobot|Wotbox|Wprecon|Xaldon WebSpider|Xaldon_WebSpider|Xenu|YaK|YoudaoBot|Zade|Zauba|Zermelo|Zeus|Zitebot|ZmEu|ZoomBot|ZoominfoBot|ZumBot|ZyBorg|adscanner|anthropic-ai|archive.org_bot|arquivo-web-crawler|arquivo.pt|autoemailspider|awario.com|backlink-check|cah.io.community|check1.exe|clark-crawler|coccocbot|cognitiveseo|cohere-ai|com.plumanalytics|crawl.sogou.com|crawler.feedback|crawler4j|dataforseo.com|dataforseobot|demandbase-bot|domainsproject.org|eCatch|evc-batch|facebookscraper|gopher|heritrix|imagesift.com|instabid|internetVista monitor|ips-agent|isitwp.com|iubenda-radar|linkdexbot|linkfluence|lwp-request|lwp-trivial|magpie-crawler|meanpathbot|mediawords|muhstik-scan|netEstate NE Crawler|oBot|omgili|openai|openai.com|page scorer|pcBrowser|plumanalytics|polaris version|probe-image-size|ripz|s1z.ru|satoristudio.net|scalaj-http|scan.lol|seobility|seocompany.store|seoscanners|seostar|serpstatbot|sexsearcher|sitechecker.pro|siteripz|sogouspider|sp_auditbot|spyfu|sysscan|tAkeOut|trendiction.com|trendiction.de|ubermetrics-technologies.com|voyagerx.com|webgains-bot|webmeup-crawler|webpros.com|webprosbot|x09Mozilla|x22Mozilla|xpymep1.exe|zauba.io|zgrab"
    }
    ]
    }
  • AI bots
    variable "ai_bots" {
    description = "Detects common AI Bot user agents."
    type = list(object({
    type = string
    op = string
    value = string
    }))
    default = [
    {
    type = "user_agent"
    op = "ex"
    value = "AI2Bot|Ai2Bot-Dolma|Amazonbot|Applebot|Applebot-Extended|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|Diffbot|FacebookBot|FriendlyCrawler|GPTBot|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|ICC-Crawler|ImagesiftBot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|PerplexityBot|PetalBot|Scrapy|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot|anthropic-ai|cohere-ai|facebookexternalhit|img2dataset|omgili|omgilibot"
    }
    ]
    }
  • OFAC countries
    variable "ofac_countries" {
    description = "Detects requests from OFAC sanctioned countries (Cuba, Iran, North Korea, Syria, Russia)."
    type = list(object({
    type = string
    op = string
    values = list(string)
    }))
    default = [
    {
    type = "geo_country"
    op = "inc"
    values = ["CU", "IR", "KP", "SY", "RU"]
    }
    ]
    }
  • WordPress URLs
    variable "wordpress_urls" {
    description = "Detects requests to WordPress URLs."
    type = list(object({
    type = string
    op = string
    value = string
    }))
    default = [
    {
    type = "path"
    op = "ex"
    value = "/(wp-admin|wp-login\\.php|xmlrpc\\.php|wp-content|wp-includes|wp-signup\\.php|wp-activate\\.php|register\\.php|wp-register\\.php)"
    }
    ]
    }
  • Rate limiting
    variable "rate_limit_condition" {
    description = "Rate limit API requests."
    type = object({
    conditions = list(object({
    type = string
    op = string
    value = string
    }))
    })
    default = {
    conditions = [
    {
    type = "path"
    op = "pre"
    value = "/api"
    }
    ]
    }
    }
    variable "rate_limit_action" {
    description = "Rate limit API requests."
    type = object({
    action = string
    rate_limit = object({
    limit = number
    window = number
    keys = list(string)
    algo = string
    action = string
    })
    action_duration = string
    })
    default = {
    action = "rate_limit"
    rate_limit = {
    limit = 100
    window = 300
    keys = ["ip", "ja4"]
    algo = "fixed_window"
    action = "deny"
    }
    action_duration = "5m"
    }
    }

These are defined once and reused across both projects.

Copy link to headingConfigure firewall rules per project

Each project (PROJECT_1, PROJECT_2) has a vercel_firewall_config that applies the rules defined in your variables.

For PROJECT_1, connect the following rules

resource "vercel_firewall_config" "project_1_fw_config" {
project_id = vercel_project.PROJECT_1.id
rules {
rule {
name = "Suspicous User Agent - Terraform"
description = "Challenge requests with suspicious user agents."
condition_group = [
{
conditions = var.suspicious_user_agent_conditions
}
]
action = {
action = "challenge"
}
}
rule {
name = "Block AI Bots - Terraform"
description = "Block requests from common AI Bot user agents."
condition_group = [
{
conditions = var.ai_bots
}
]
action = {
action = "challenge"
}
}
rule {
name = "Block OFAC Countries - Terraform"
description = "Block requests from OFAC sanctioned countries."
condition_group = [
{
conditions = var.ofac_countries
}
]
action = {
action = "deny"
}
}
rule {
name = "Block WordPress URLs - Terraform"
description = "Block requests to WordPress URLs."
condition_group = [
{
conditions = var.wordpress_urls
}
]
action = {
action = "deny"
}
}
rule {
name = "Rate limit API"
description = "apply ratelimit to requests under /api"
condition_group = [{
conditions = var.rate_limit_condition.conditions
}]
action = var.rate_limit_action
}
}
}

And for PROJECT_2, connect the following rules:

resource "vercel_firewall_config" "project_2_waf_config" {
project_id = vercel_project.PROJECT_2.id
rules {
rule {
name = "Suspicous User Agent - Terraform"
description = "Challenge requests with suspicious user agents."
condition_group = [
{
conditions = var.suspicious_user_agent_conditions
}
]
action = {
action = "challenge"
}
}
rule {
name = "Block AI Bots - Terraform"
description = "Block requests from common AI Bot user agents."
condition_group = [
{
conditions = var.ai_bots
}
]
action = {
action = "challenge"
}
}
rule {
name = "Block OFAC Countries - Terraform"
description = "Block requests from OFAC sanctioned countries."
condition_group = [
{
conditions = var.ofac_countries
}
]
action = {
action = "deny"
}
}
rule {
name = "Block WordPress URLs - Terraform"
description = "Block requests to WordPress URLs."
condition_group = [
{
conditions = var.wordpress_urls
}
]
action = {
action = "deny"
}
}
rule {
name = "Rate limit API"
description = "apply ratelimit to requests under /api"
condition_group = [{
conditions = var.rate_limit_condition.conditions
}]
action = var.rate_limit_action
}
}
}

This structure lets you enforce a consistent policy across multiple projects.

Copy link to headingInitialize and Apply

Run the following from the command line:

terraform init
terraform plan
terraform apply

This will apply the rules defined for each project as defined for PROJECT_1 and PROJECT_2. Terraform loads all .tf files in the current working directory, merges them, and treats them as a single configuration.

Copy link to headingRecommendations

  • Test with terraform plan to preview changes before applying.
  • Store the project IDs and sensitive values using environment variables.

Related documentation

More Firewall guides