---
title: "Harden WordPress with Cloudflare Free Plan Using Terraform | CloudGuys Blog"
description: "Maximize Cloudflare’s Free tier for WordPress using OpenTofu/Terraform. Learn how to provision 77+ resources, including WAF rules, Cache Rules, and security headers, via Infrastructure as Code - without spending a dime."
url: "https://cloudguys.io/blog/harden-wordpress-with-cloudflare-free-plan-using-terraform"
language: "en"
image: "https://cloudguys.io/_astro/cover.B4LJ10cr.png"
published: "2026-05-13T00:00:00.000Z"
modified: "2026-05-13T00:00:00.000Z"
---

ENGINEERING NOTES / DEVOPS, IAC

# Harden WordPress with Cloudflare Free Plan Using Terraform

Maximize Cloudflare’s Free tier for WordPress using OpenTofu/Terraform. Learn how to provision 77+ resources, including WAF rules, Cache Rules, and security headers, via Infrastructure as Code - without spending a dime.

Agrohi Team · · · May 13, 2026 · · · 8 min

![Harden WordPress with Cloudflare Free Plan Using Terraform](https://cloudguys.io/_astro/cover.B4LJ10cr_1rsjrq.webp)

*A technical deep-dive into maximizing Cloudflare’s free-tier security and performance for WordPress, managed entirely through Infrastructure as Code.*

At [Agrohi](https://cloudguys.io/), we manage multiple WordPress properties. Every one of them faces the same reality: brute-force login attempts, XML-RPC abuse, comment spam bots, sensitive file probes, and the occasional credential-stuffing burst — all before breakfast.

We needed a security baseline that was **repeatable across zones**, **free**, and **didn’t break payment webhooks**. Cloudflare’s Free plan turned out to be far more capable than most people realize — if you know where to look.

This post walks through how we built an OpenTofu module that provisions **77 Cloudflare resources per zone** on the Free plan, covering WAF rules, cache optimization, security headers, rate limiting, and more — all without spending a dollar on Cloudflare.

## The Problem: WordPress Is a Target by Default

A fresh WordPress install exposes several well-known attack surfaces:

-   `/wp-login.php` Brute-force and credential stuffing
-   `/xmlrpc.php` Amplification attacks, brute-force via `system.multicall`
-   `/wp-comments-post.php` Automated spam submissions
-   `/wp-config.php`, `.env`, `.git/` Sensitive file probes
-   `/?author=N` Username enumeration via author archives

Most WordPress hardening guides tell you to install a security plugin. That’s fine for a single site. But when you’re managing multiple zones, you need something deterministic, version-controlled, and repeatable.

## The Approach: Infrastructure as Code with Cloudflare’s Free Tier

We chose [OpenTofu](https://opentofu.org/) (the open-source Terraform fork) with the [Cloudflare provider v5](https://registry.terraform.io/providers/cloudflare/cloudflare/latest). The architecture is simple:

```plaintext
root module (multi-zone)
  └── modules/wp_cloudflare_zone (reusable per-zone module)
        ├── cache_rules.tf
        ├── config_rules.tf
        ├── waf_custom.tf
        ├── rate_limit.tf
        ├── transform_rules.tf
        ├── redirect_rules.tf
        ├── zone_settings.tf
        ├── access_rules.tf
        ├── dns.tf
        └── managed_waf.tf
```

One `terraform.tfvars` file. One `tofu apply`. Every zone gets the same hardened baseline.

## What Most People Don’t Know: The Free Plan Quota Budget

Here’s the part that surprises people. Cloudflare’s Free plan gives you **far more than 5 WAF rules and 3 page rules**. The full entitlement looks like this:

| **Resource Type** | **Free Limit** | **What It Does** |
| --- | --- | --- |
| WAF Custom Rules | **5** | Block, challenge, or skip based on request attributes |
| Rate Limiting Rules | **1** | Throttle by IP per time window |
| Cache Rules | **10** | Control caching behavior per-path (replaces Page Rules) |
| Configuration Rules | **10** | Override zone settings per-path |
| Transform Rules | **10** | Modify request/response headers at the edge |
| Redirect Rules | **10** | URL redirects without touching your origin |
| Origin Rules | **10** | Override origin hostname, port, SNI |
| Access Rules | **Unlimited** | IP/CIDR allowlist/blocklist |

**Page Rules are deprecated.** Cloudflare stopped accepting new ones in January 2025. If your IaC is still uses `cloudflare_page_rule`, it will fail on new zones. The replacement is Cache Rules, which gives you **10 rules** with expression-based matching instead of glob patterns.

Most WordPress-on-Cloudflare setups we’ve seen use maybe 15% of this capacity. We aimed for 60%+.

## Layer 1: TLS & Transport Baseline

The foundation. Six `cloudflare_zone_setting` resources that enforce:

```plaintext
ssl                      = "strict"
min_tls_version          = "1.2"
tls_1_3                  = "on"
always_use_https         = "on"
automatic_https_rewrites = "on"
security_level           = "medium"
```

This eliminates mixed-content issues, enforces encrypted transport end-to-end, and sets a baseline challenge sensitivity. These settings persist even if you tear down your Terraform state; they’re zone-level toggles, not discrete objects.

## Layer 2: WAF Custom Rules (5 of 5 Used)

The 5-rule limit is tight, so consolidation is key. We pack maximum coverage into each rule by OR-ing conditions together.

**Rule 1 - Admin Perimeter Challenge:**  
Applies `managed_challenge` to `/wp-login.php` and `/wp-admin/*`, but **exempts** `/wp-admin/admin-ajax.php` and `/wp-admin/admin-post.php` (which WordPress themes and plugins hit legitimately from the frontend).

**Rule 2 - XML-RPC Block:**  
Hard blocks `/xmlrpc.php`. If you’re using Jetpack or the WordPress mobile app, you’d toggle this off per-zone.

**Rule 3 - Comment Spam Block:**  
Blocks direct `POST` to `/wp-comments-post.php` when the `Referer` header doesn’t contain the site’s own domain. Legitimate comment submissions always come from the site itself.

**Rule 4 - Bad UA + Sensitive Probe Block:**  
A single rule that catches both:

-   17 known-bad user-agent signatures (`python`, `curl`, `sqlmap`, `nikto`, `wpscan`, `nuclei`, etc.)
-   10 sensitive file probe tokens (`wp-config.php`, `.env`, `.git/`, `.htaccess`, `debug.log`, `phpinfo.php`, etc.)

Empty user-agents are also caught. Since these are OR’d into one expression, they consume only **1 rule slot**.

**Rule 5 - Geo Challenge (optional):**  
Apply `managed_challenge` to traffic from outside a whitelist of allowed countries. Off by default because it’s aggressive, but available when needed.

### The Critical Detail: Webhook Exclusions

Every single custom rule includes a webhook exclusion expression:

```plaintext
not (starts_with(http.request.uri.path, "/webhooks/stripe") or
     starts_with(http.request.uri.path, "/webhooks/paypal") or
     starts_with(http.request.uri.path, "/wc-api/"))
```

Payment callbacks from Stripe, PayPal, and Square are server-to-server. They often come with empty or non-browser user-agents. Without explicit exclusions, your WAF rules **will** break payment processing and cause order-state drift.

This is the single most important operational detail in the entire setup.

## Layer 3: Cache Rules (6 of 10 Used)

These replace the deprecated Page Rules. We use 6 of the 10 available slots:

| **Rule** | **Expression** | **Action** |
| --- | --- | --- |
| `cache_bypass_wp_admin` | `starts_with(path, "/wp-admin/")` | Bypass cache |
| `cache_bypass_wp_login` | `path eq "/wp-login.php"` | Bypass cache |
| `cache_bypass_webhooks` | Webhook path expressions | Bypass cache |
| `cache_bypass_wc_ajax` | `query contains "wc-ajax="` | Bypass cache |
| `cache_bypass_wp_cron` | `path eq "/wp-cron.php"` | Bypass cache |
| `cache_static_assets` | File extensions (css, js, images, fonts) | Cache with 1-day edge TTL |

The static asset rule is the performance win - CSS, JS, images, and fonts get cached at Cloudflare’s edge with a 24-hour TTL and 4-hour browser TTL. This alone measurably improves TTFB for returning visitors.

## Layer 4: Configuration Rules (4 of 10 Used)

Configuration Rules let you override zone-level settings on a per-path basis. We use 4:

1.  **Disable BIC on webhooks**: Browser Integrity Check kills server-to-server callbacks
2.  **Disable email obfuscation on webhooks**: Cloudflare’s email obfuscation rewrites email addresses in HTML responses; this corrupts JSON payloads that contain email fields
3.  **Disable Rocket Loader on wp-admin**: Rocket Loader async-wraps all `<script>` tags, which breaks WordPress admin JavaScript
4.  **Elevate security level on wp-login**: Sets security to “high” specifically on the login page, adding extra challenge sensitivity where it matters most

## Layer 5: Security Response Headers (1 of 10 Transform Rules)

A single transform rule that modifies response headers on **every response**:

```plaintext
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
```

It also **strips the **`X-Powered-By`** header**, which typically reveals your PHP version.

This is free security hardening that requires **zero changes to your origin server**. The headers are injected at Cloudflare’s edge.

## Layer 6: Rate Limiting (1 of 1 Used)

The Free plan gives you exactly **one** rate-limiting rule. We make it count by covering both high-abuse POST endpoints in a single expression:

```plaintext
(path eq "/wp-login.php" or path eq "/wp-comments-post.php")
and method eq "POST"
```

Characteristics: `cf.colo.id` + `ip.src` (rate limit per source IP per colo).  
Threshold: 20 requests per 10 seconds, with a 10-second block.

The `period = 10` and `mitigation_timeout = 10` values are enforced by validation - they’re the only values the Free plan supports.

## Layer 7: Redirect Rules (1 of 10 Used)

**Author enumeration prevention.** WordPress exposes usernames via `/?author=1`, `/?author=2`, etc. Attackers use this to build username lists for brute-force attacks.

Our redirect rule catches any request with `author=` in the query string and 301-redirects it to the homepage. Simple, effective, no origin hit.

## Layer 8: Payment Gateway IP Allowlist

For WooCommerce sites, we optionally create `cloudflare_access_rule` entries that whitelist known payment gateway source IPs:

-   \*\*Stripe \*\*- 15 IPs
-   **PayPal** - 8 CIDRs (expanded to /24 blocks for Cloudflare compatibility)
-   **Square** - 2 production + 2 optional sandbox IPs
-   **Skrill** - 6 IPs

The module auto-detects single IPs vs CIDRs and normalizes non-standard prefix lengths (`/17`–`/23`) into exact `/24` blocks, since Cloudflare access rules only accept `/16` or `/24` for ranges.

> **Important:** IP allowlisting is defense-in-depth, not a substitute for webhook signature verification in your application code. Payment provider IPs can change.

## The Guardrails

We enforce Free-plan safety at the validation layer, not at runtime:

```plaintext
# Custom rule count hard limit
lifecycle {
  precondition {
    condition     = length(local.custom_rules) <= 5
    error_message = "Free-plan custom rules exceeded (max 5)."
  }
}

# Rate limit period must be 10 (Free plan constraint)
validation {
  condition     = var.login_rate_limit.period == 10
  error_message = "Free-plan rate limiting requires period = 10."
}
```

If you accidentally enable too many optional custom rules (geo challenge + geo block + everything else), `tofu plan` fails with a clear message **before** touching the API.

## What We Left on the Table

We’re deliberately not using:

-   **Origin Rules** (10 available): Useful for host header rewriting in multi-site setups, but not needed for standard WordPress
-   **Request Header Transform Rules** (10 available): Could add `X-Real-IP` forwarding or custom headers, reserved for site-specific needs
-   **Managed WAF**: Cloudflare provides a free managed ruleset (`efb7b8c949ac4650a09736fc376e9aee`) with basic OWASP and WordPress-specific protections. We keep it opt-in because it can cause false positives with some plugins

## The Result

One `tofu apply`, **77 resources per zone**:

```plaintext
Plan: 77 to add, 0 to change, 0 to destroy.
```

Every WordPress zone gets:

-   ✅ Strict TLS + forced HTTPS at the edge
-   ✅ Managed challenge on admin endpoints
-   ✅ XML-RPC blocked
-   ✅ Comment spam filtered
-   ✅ 17 scanner user-agents blocked
-   ✅ 10 sensitive file probes blocked
-   ✅ Login + comment rate limiting
-   ✅ Cache bypass on dynamic paths
-   ✅ Static asset edge caching
-   ✅ Security response headers (no origin changes)
-   ✅ Author enumeration prevented
-   ✅ Payment webhook paths excluded from all security rules
-   ✅ Payment gateway IPs allowlisted

All on the Free plan. All version-controlled. All repeatable across zones.

## Getting Started

The module is designed for multi-zone use. A minimal `terraform.tfvars`:

```plaintext
zones = {
  my_site = {
    zone_id   = "your-zone-id"
    zone_name = "yourdomain.com"

    dns_records = [{
      name    = "@"
      type    = "A"
      content = "your.server.ip"
      proxied = true
    }]

    payment_webhook_paths = [
      "/webhooks/stripe",
      "/wc-api/"
    ]
  }
}
```

Everything else has sensible defaults. Run `tofu init && tofu plan` and review.

## Key Takeaways

1.  **Cloudflare’s Free plan is massively underutilized.** Most setups use 3 Page Rules and call it done. You have 50+ rule slots across 7 rule types.
2.  **Page Rules are dead.** Migrate to Cache Rules now or your IaC will break on new zones.
3.  **Webhook safety is non-negotiable.** Every security rule must exclude payment callback paths.
4.  **Transform Rules are free security headers.** `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy` are all injected at the edge, zero origin changes.
5.  **IaC makes multi-zone manageable.** One module, one apply, deterministic state across every property.

*This setup powers the WordPress infrastructure of *[*Agrohi’s Clients*](https://cloudguys.io/)*. We’re a media and technology group focused on building resilient, cost-effective web properties. If you’re managing WordPress at scale and want to talk shop, reach out.*

#WordPress · #Cloudflare · #InfrastructureAsCode · #OpenTofu · #Terraform · #WebSecurity · #FreeTier · #DevOps · #WAF · #CDN · #Agrohi

A FRESH PERSPECTIVE ON YOUR CLOUD

## Great engineering starts  
with a good conversation.

Let’s talk about what’s working, what’s slowing you down, and what comes next.

[Talk to an engineer ↗](mailto:hello@agrohi.com?subject=CloudGuys%20assessment%20enquiry)

## Structured data

```json
[
  {
    "@context": "https://schema.org",
    "@type": "Organization",
    "@id": "https://cloudguys.io/#organization",
    "name": "CloudGuys",
    "url": "https://cloudguys.io/",
    "logo": {
      "@type": "ImageObject",
      "url": "https://cloudguys.io/assets/logo.png",
      "width": 512,
      "height": 512
    },
    "image": "https://cloudguys.io/assets/og-image.png",
    "description": "Security assessments, cloud architecture reviews, and practical remediation across AWS, Google Cloud, Azure, Kubernetes, and infrastructure as code.",
    "email": "hello@agrohi.com",
    "slogan": "Know your risks. Build a stronger cloud.",
    "areaServed": "Worldwide",
    "knowsAbout": [
      "DevOps",
      "Kubernetes",
      "Terraform",
      "Cloud migration",
      "Cloud cost optimization",
      "GitOps",
      "Site reliability engineering",
      "Kubernetes dashboard",
      "AI SRE",
      "Kubernetes troubleshooting",
      "AWS",
      "Google Cloud",
      "Azure"
    ],
    "contactPoint": {
      "@type": "ContactPoint",
      "contactType": "sales",
      "email": "hello@agrohi.com",
      "availableLanguage": [
        "English"
      ],
      "areaServed": "Worldwide"
    },
    "sameAs": [
      "https://www.linkedin.com/company/agrohitech",
      "https://berth.agrohi.com"
    ],
    "owns": {
      "@type": "SoftwareApplication",
      "@id": "https://berth.agrohi.com/#software",
      "name": "Berth",
      "applicationCategory": "DeveloperApplication",
      "operatingSystem": "Kubernetes",
      "url": "https://berth.agrohi.com",
      "description": "Berth is a self-hosted Kubernetes dashboard with a read-only AI SRE: endpoint health, capacity planning, guided app exposure, and evidence-based troubleshooting, run in your own cluster."
    }
  },
  {
    "@context": "https://schema.org",
    "@type": "WebSite",
    "@id": "https://cloudguys.io/#website",
    "url": "https://cloudguys.io/",
    "name": "CloudGuys",
    "description": "Security and cloud consultancy helping organizations assess risks, improve architecture, and verify remediation.",
    "publisher": {
      "@id": "https://cloudguys.io/#organization"
    },
    "inLanguage": "en"
  },
  {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "@id": "https://cloudguys.io/blog/harden-wordpress-with-cloudflare-free-plan-using-terraform#article",
    "headline": "Harden WordPress with Cloudflare Free Plan Using Terraform",
    "description": "Maximize Cloudflare’s Free tier for WordPress using OpenTofu/Terraform. Learn how to provision 77+ resources, including WAF rules, Cache Rules, and security headers, via Infrastructure as Code - without spending a dime.",
    "image": "https://cloudguys.io/_astro/cover.B4LJ10cr.png",
    "datePublished": "2026-05-13T00:00:00.000Z",
    "dateModified": "2026-05-13T00:00:00.000Z",
    "author": {
      "@type": "Organization",
      "name": "Agrohi Team",
      "url": "https://cloudguys.io/"
    },
    "publisher": {
      "@id": "https://cloudguys.io/#organization"
    },
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "https://cloudguys.io/blog/harden-wordpress-with-cloudflare-free-plan-using-terraform"
    },
    "articleSection": "DevOps, IaC",
    "keywords": "WordPress, Cloudflare, InfrastructureAsCode, OpenTofu, Terraform, WebSecurity, FreeTier, DevOps, WAF, CDN, Agrohi",
    "inLanguage": "en"
  },
  {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
      {
        "@type": "ListItem",
        "position": 1,
        "name": "Home",
        "item": "https://cloudguys.io/"
      },
      {
        "@type": "ListItem",
        "position": 2,
        "name": "Blog",
        "item": "https://cloudguys.io/blog"
      },
      {
        "@type": "ListItem",
        "position": 3,
        "name": "Harden WordPress with Cloudflare Free Plan Using Terraform",
        "item": "https://cloudguys.io/blog/harden-wordpress-with-cloudflare-free-plan-using-terraform"
      }
    ]
  }
]
```
