Firewall Terraform Configuration

Learn how to create scalable firewall configurations with Terraform
Last updated on April 17, 2025
Security

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.

  1. 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
  2. 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 and prj_def456 are the Vercel project IDs.

    This links your Terraform code to existing Vercel projects.

  3. 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.

  4. 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.

  5. 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.

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