--- name: cloudflare-coder description: This skill guides provisioning Cloudflare infrastructure with OpenTofu/Terraform. Use when managing zones, DNS records, WAF rules, SSL settings, Page Rules, or cache configuration. allowed-tools: Read, Write, Edit, Grep, Glob, Bash --- # Cloudflare Coder ## Overview Cloudflare provides CDN, DNS, security, and edge computing services. This skill covers OpenTofu/Terraform patterns for Cloudflare resources. ## Provider Setup ```hcl terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" version = "~> 4.0" } } } provider "cloudflare" { # API Token from environment: CLOUDFLARE_API_TOKEN # Or explicitly (not recommended): # api_token = var.cloudflare_api_token } ``` ### Authentication ```bash # Preferred: API Token (scoped permissions) export CLOUDFLARE_API_TOKEN="your-api-token" # Or with 1Password CLOUDFLARE_API_TOKEN=op://Infrastructure/Cloudflare/api_token # Legacy: Global API Key (full access - avoid if possible) export CLOUDFLARE_API_KEY="your-global-api-key" export CLOUDFLARE_EMAIL="your-email@example.com" ``` **API Token Permissions (minimum required):** | Resource | Permission | Use Case | |----------|------------|----------| | Zone | Read | List zones, read settings | | Zone | Edit | Modify zone settings | | DNS | Edit | Manage DNS records | | Firewall | Edit | WAF, firewall rules | | SSL | Edit | Certificate management | | Cache | Purge | Cache invalidation | ## Data Sources ### Get Zone by Name ```hcl data "cloudflare_zone" "main" { name = "example.com" } # Use in resources resource "cloudflare_record" "www" { zone_id = data.cloudflare_zone.main.id # ... } ``` ### Get Account ID ```hcl data "cloudflare_accounts" "main" { name = "My Account" } output "account_id" { value = data.cloudflare_accounts.main.accounts[0].id } ``` ## DNS Records ### Basic Records ```hcl # A Record resource "cloudflare_record" "root" { zone_id = data.cloudflare_zone.main.id name = "@" type = "A" content = var.server_ip ttl = 1 # 1 = automatic proxied = true } # CNAME Record resource "cloudflare_record" "www" { zone_id = data.cloudflare_zone.main.id name = "www" type = "CNAME" content = "@" ttl = 1 proxied = true } # MX Records resource "cloudflare_record" "mx_primary" { zone_id = data.cloudflare_zone.main.id name = "@" type = "MX" content = "mail.example.com" ttl = 3600 priority = 10 proxied = false # MX cannot be proxied } # TXT Record (SPF) resource "cloudflare_record" "spf" { zone_id = data.cloudflare_zone.main.id name = "@" type = "TXT" content = "v=spf1 include:_spf.google.com ~all" ttl = 3600 } ``` ### DNS Records with for_each ```hcl locals { dns_records = { api = { type = "A" content = var.api_server_ip proxied = true } staging = { type = "A" content = var.staging_server_ip proxied = true } mail = { type = "A" content = var.mail_server_ip proxied = false } } } resource "cloudflare_record" "subdomain" { for_each = local.dns_records zone_id = data.cloudflare_zone.main.id name = each.key type = each.value.type content = each.value.content ttl = 1 proxied = each.value.proxied } ``` ## SSL/TLS Settings ### Zone SSL Settings ```hcl resource "cloudflare_zone_settings_override" "ssl" { zone_id = data.cloudflare_zone.main.id settings { # SSL mode: off, flexible, full, strict (recommended) ssl = "strict" # Minimum TLS version min_tls_version = "1.2" # Always use HTTPS always_use_https = "on" # Automatic HTTPS Rewrites automatic_https_rewrites = "on" # HTTP Strict Transport Security security_header { enabled = true max_age = 31536000 # 1 year include_subdomains = true preload = true nosniff = true } # TLS 1.3 tls_1_3 = "on" # Opportunistic Encryption opportunistic_encryption = "on" } } ``` ### Origin Certificates ```hcl resource "cloudflare_origin_ca_certificate" "origin" { csr = tls_cert_request.origin.cert_request_pem hostnames = ["example.com", "*.example.com"] request_type = "origin-rsa" requested_validity = 5475 # 15 years (max) } # Generate private key and CSR resource "tls_private_key" "origin" { algorithm = "RSA" rsa_bits = 2048 } resource "tls_cert_request" "origin" { private_key_pem = tls_private_key.origin.private_key_pem subject { common_name = "example.com" organization = "Example Inc" } } output "origin_certificate" { value = cloudflare_origin_ca_certificate.origin.certificate sensitive = true } ``` ## Firewall & WAF See [resources/waf.md](resources/waf.md) for IP access rules, WAF custom rules with rate limiting, and managed rulesets (Cloudflare + OWASP). ## Cache Rules ### Modern Cache Rules (Rulesets) ```hcl resource "cloudflare_ruleset" "cache" { zone_id = data.cloudflare_zone.main.id name = "Cache Rules" description = "Caching configuration" kind = "zone" phase = "http_request_cache_settings" # Cache static assets aggressively rules { action = "set_cache_settings" action_parameters { cache = true edge_ttl { mode = "override_origin" default = 2592000 # 30 days } browser_ttl { mode = "override_origin" default = 86400 # 1 day } } expression = "(http.request.uri.path.extension in {\"css\" \"js\" \"jpg\" \"jpeg\" \"png\" \"gif\" \"svg\" \"woff\" \"woff2\"})" description = "Cache static assets" enabled = true } # Bypass cache for API rules { action = "set_cache_settings" action_parameters { cache = false } expression = "(starts_with(http.request.uri.path, \"/api/\"))" description = "Bypass cache for API" enabled = true } # Bypass cache for admin rules { action = "set_cache_settings" action_parameters { cache = false } expression = "(starts_with(http.request.uri.path, \"/admin\"))" description = "Bypass cache for admin" enabled = true } } ``` ### Legacy Page Rules ```hcl # Cache everything for static paths resource "cloudflare_page_rule" "cache_static" { zone_id = data.cloudflare_zone.main.id target = "example.com/assets/*" priority = 1 actions { cache_level = "cache_everything" edge_cache_ttl = 2592000 # 30 days } } # Bypass cache for API resource "cloudflare_page_rule" "bypass_api" { zone_id = data.cloudflare_zone.main.id target = "example.com/api/*" priority = 2 actions { cache_level = "bypass" } } # Force HTTPS resource "cloudflare_page_rule" "force_https" { zone_id = data.cloudflare_zone.main.id target = "http://example.com/*" priority = 3 actions { always_use_https = true } } ``` ## Redirect Rules ```hcl resource "cloudflare_ruleset" "redirects" { zone_id = data.cloudflare_zone.main.id name = "Redirects" description = "URL redirects" kind = "zone" phase = "http_request_dynamic_redirect" # www to apex redirect rules { action = "redirect" action_parameters { from_value { status_code = 301 target_url { expression = "concat(\"https://example.com\", http.request.uri.path)" } preserve_query_string = true } } expression = "(http.host eq \"www.example.com\")" description = "Redirect www to apex" enabled = true } # Old URL to new URL rules { action = "redirect" action_parameters { from_value { status_code = 301 target_url { value = "https://example.com/new-page" } } } expression = "(http.request.uri.path eq \"/old-page\")" description = "Redirect old page to new" enabled = true } } ``` ## Zone Settings ### Performance Settings ```hcl resource "cloudflare_zone_settings_override" "performance" { zone_id = data.cloudflare_zone.main.id settings { # Compression brotli = "on" # Speed optimizations minify { css = "on" html = "on" js = "on" } # Early Hints early_hints = "on" # HTTP/2 and HTTP/3 http2 = "on" http3 = "on" # 0-RTT zero_rtt = "on" # Rocket Loader (async JS loading) rocket_loader = "on" # Image optimization (requires Pro+) # polish = "lossless" # mirage = "on" # Mobile redirect (if needed) # mobile_redirect { # mobile_subdomain = "m" # status = "on" # strip_uri = false # } } } ``` ### Security Settings ```hcl resource "cloudflare_zone_settings_override" "security" { zone_id = data.cloudflare_zone.main.id settings { # Security level: off, essentially_off, low, medium, high, under_attack security_level = "medium" # Challenge Passage TTL challenge_ttl = 1800 # Browser Integrity Check browser_check = "on" # Email Obfuscation email_obfuscation = "on" # Server Side Excludes server_side_exclude = "on" # Hotlink Protection hotlink_protection = "on" # IP Geolocation ip_geolocation = "on" } } ``` ## Argo & Load Balancing See [resources/load-balancer.md](resources/load-balancer.md) for Argo Smart Routing and Load Balancer configuration with origin pools, health checks, and session affinity. ## Workers Routes ```hcl # Deploy Worker via Terraform (see wrangler-coder for full Worker management) resource "cloudflare_worker_route" "api" { zone_id = data.cloudflare_zone.main.id pattern = "example.com/api/*" script_name = cloudflare_worker_script.api.name } resource "cloudflare_worker_script" "api" { account_id = var.cloudflare_account_id name = "api-worker" content = file("${path.module}/workers/api.js") module = true plain_text_binding { name = "ENVIRONMENT" text = var.environment } secret_text_binding { name = "API_KEY" text = var.api_key } } ``` ## Complete Production Zone See [resources/production-zone.md](resources/production-zone.md) for a complete Terraform configuration with DNS, SSL/TLS, WAF, caching, redirects, and variables template. ## Best Practices - **Use API Tokens** - Scoped permissions over global API key - **Enable Strict SSL** - Always use Full (Strict) mode with valid origin certs - **Use Rulesets** - Modern rulesets over legacy Page Rules - **Cache Strategically** - Static assets cached, dynamic bypassed - **Rate Limit APIs** - Protect endpoints from abuse - **Enable WAF** - Deploy managed rulesets as baseline - **HSTS Preload** - Add to browser preload list for max security - **Terraform State** - Store state securely, not in version control