ℹ️ Many blog posts do not include full scripts. If you require a complete version, please use the Support section in the menu.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Migrating 40+ Web Forwards to Cloudflare: A Bulk Redirect Adventure

I recently needed to migrate over 40 subdomain redirects from my domain registrar's web forwarding service to Cloudflare. What seemed like a straightforward task turned into an educational journey through Cloudflare's API limitations and redirect systems. Here's what I learned.

The Goal

Replace my registrar's web forwarding rules with Cloudflare-managed redirects for 40+ subdomains across multiple domains (a6n.co.uk, this testing was done with pokebearswithsticks.com). Each subdomain needed to redirect to various destinations - some to Azure static sites, others to external services.

Example redirects:

  • legal.a6n.co.ukhttps://bloguk2.z13.web.core.windows.net/legal.html
  • service.a6n.co.ukhttps://a6n.statuspage.io/
  • support.a6n.co.uk → Google Forms URL

Visual End Result

This shows the zones all added to Cloudflare with the "proxy" icon to confirm those records are being proxied:


Then we have the bulk redirection options which you can find under Rules>Settings as below:


First you need the set the "Bulk Redirect Lists" which will be inactive, as you can see they are active here:

Those lists are active because they are linked to a Bulk Redirect Rule that is enabled:

If we look at the a6n.co.uk rule you can see that the rule links to the list as below:


Initial Approach: Single Redirects

My first instinct was to use Cloudflare's new Single Redirects feature, which replaced the deprecated Page Rules. I wrote a PowerShell script to batch-add all redirects via the API:

foreach ($redirect in $redirects) {
    $rule = @{
        action = "redirect"
        action_parameters = @{
            from_value = @{
                status_code = $redirect.status
                target_url = @{
                    value = $redirect.destination
                }
                preserve_query_string = $false
            }
        }
        expression = "(http.host eq `"$($redirect.subdomain).a6n.co.uk`")"
        description = "Redirect $($redirect.subdomain).a6n.co.uk"
        enabled = $true
    }
    $rules += $rule
}

Lesson 1: Know Your Limits

The script failed immediately with a clear error message:

{
  "errors": [{
    "code": 50001,
    "message": "exceeded the maximum number of rules in the phase http_request_dynamic_redirect: 40 out of 10"
  }]
}

Key Learning: Cloudflare's free plan limits Single Redirects to just 10 rules. This wasn't immediately obvious from the dashboard, which happily let me start creating rules without warning about the limit.

Pivot to Bulk Redirects

After discovering the 10-rule limit, I learned about Bulk Redirects - a different system that handles unlimited redirects on all plans. However, this required a complete rewrite since Bulk Redirects work differently:

  1. Create a list of redirect mappings
  2. Add items to the list
  3. Create a rule to activate the list

Lesson 2: API Authentication Methods Matter

When trying to update existing rulesets, I hit another roadblock:

{
  "errors": [{
    "code": 10000,
    "message": "PATCH method not allowed for the api_token authentication scheme"
  }]
}

Key Learning: Not all HTTP methods are available with API token authentication. I had to use PUT with complete payloads instead of PATCH for partial updates:

# Wrong - PATCH not allowed with API tokens
$response = Invoke-RestMethod -Uri $updateUrl -Method Patch -Headers $headers -Body $updateBody

# Correct - PUT with complete ruleset
$updateBody = @{
    description = $currentRuleset.result.description
    kind = "root"
    phase = "http_request_redirect"
    rules = $updatedRules
} | ConvertTo-Json -Depth 10

$response = Invoke-RestMethod -Uri $updateUrl -Method Put -Headers $headers -Body $updateBody

Lesson 3: Naming Conventions Are Strict

Creating the Bulk Redirect list failed with an unhelpful error:

{
  "errors": [{
    "code": 10029,
    "message": "invalid_name"
  }]
}

Key Learning: Cloudflare list names must use only alphanumeric characters and underscores. No hyphens allowed:

# Wrong
$listName = "a6n-redirects-20241228"

# Correct
$listName = "a6n_redirects_20241228"

Lesson 4: API Permissions Are Nuanced

The permissions required for different operations aren't always what you'd expect:

  • Single Redirects: Requires "Zone > Single Redirect > Edit"
  • Bulk Redirects: Requires "Account > Account Filter Lists > Edit" and "Account > Account Rulesets > Edit"

Note that Bulk Redirects operate at the account level, not zone level, which is why they need different permissions.

The Complete Solution

Here's the working approach for Bulk Redirects:

# Step 1: Create DNS records (required for redirects to work)
foreach ($subdomain in $subdomains) {
    $body = @{
        type = "A"
        name = $subdomain
        content = "192.0.2.1"  # Dummy IP - never reached due to redirect
        ttl = 1
        proxied = $true  # Must be proxied for redirects to work
    } | ConvertTo-Json
}

# Step 2: Create Bulk Redirect list
$listName = "a6n_redirects_$(Get-Date -Format 'yyyyMMddHHmmss')"
$createListBody = @{
    name = $listName
    description = "Redirect list for a6n.co.uk subdomains"
    kind = "redirect"
} | ConvertTo-Json

# Step 3: Add redirect items
$items = @()
foreach ($redirect in $redirects) {
    $items += @{
        redirect = @{
            source_url = "https://$($redirect.source)/"
            target_url = $redirect.target
            status_code = $redirect.status
            preserve_query_string = $false
            include_subdomains = $false
            subpath_matching = $false
            preserve_path_suffix = $false
        }
    }
}

# Step 4: Create rule to activate the list
$bulkRule = @{
    action = "redirect"
    action_parameters = @{
        from_list = @{
            name = $listName
            key = "http.request.full_uri"
        }
    }
    expression = "http.request.full_uri in `$" + $listName
    description = "Bulk redirects for a6n.co.uk"
    enabled = $true
}

Lesson 5: DNS Records Are Essential

One crucial detail that's easy to miss: redirects only work if DNS records exist and are proxied through Cloudflare. Each subdomain needs an A record pointing to a dummy IP (192.0.2.1) with the proxy enabled (orange cloud). Without this, Cloudflare never sees the traffic to redirect it.

Conclusions

  1. Check limits before implementation - Single Redirects seem perfect until you hit the 10-rule limit
  2. Understand the API authentication model - Not all HTTP methods work with API tokens
  3. Pay attention to naming conventions - "invalid_name" errors could be more descriptive
  4. Account vs Zone permissions - Different Cloudflare features operate at different levels
  5. Don't forget the DNS - Redirects need proxied DNS records to function

The complete PowerShell scripts are available if you need to do a similar migration. While the journey had its frustrations, Bulk Redirects proved to be a powerful solution that actually exceeded my original requirements by removing the redirect limit entirely.

Previous Post Next Post

نموذج الاتصال