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.
- A Vercel account with access to the projects you want to manage.
- The Terraform CLI installed. Follow the Install Terraform guide to do this for all operating systems.
- A Vercel personal token to authenticate with the Terraform provider.
- The Vercel Terraform Provider installed.
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 Vercel 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
If your Vercel 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
andprj_def456
are the Vercel project IDs.This links your Terraform code to existing Vercel projects.
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.
Each project (
PROJECT_1
,PROJECT_2
) has avercel_firewall_config
that applies the rules defined in your variables.For
PROJECT_1
, connect the following rulesresource "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.
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
andPROJECT_2
. Terraform loads all.tf
files in the current working directory, merges them, and treats them as a single configuration.
- Test with
terraform plan
to preview changes before applying. - Store the project IDs and sensitive values using environment variables.