Cloudflare Redirect Migration Plan
This document outlines the strategy for migrating redirects from Rack::Rewrite (Rails middleware) to Cloudflare edge.
Current State
File: config/initializers/260_redirections.rb
| Metric | Count |
|---|---|
| Total r301 redirects | 883 |
| Lambda-based (dynamic) | 3 |
| Country-based (geo) | 4 |
| Host-based (subdomain) | 14 |
| Scheme-based (http→https) | 4 |
| Rewrite rules | 5 |
Benefits of Edge Redirects
- Performance - Redirects happen at Cloudflare edge (< 10ms) vs origin (100-300ms)
- Reduced Origin Load - Redirect requests never hit Rails
- Better SEO - Faster redirects improve crawler efficiency
- Edge Caching - Redirects can be cached at edge
- Simplified Middleware - Less Rack middleware to process
Migration Categories
Category 1: Bulk Redirects (Simple Static)
~850 redirects → Cloudflare Bulk Redirects
These are simple path-to-path mappings with no dynamic logic:
r301 %r{/old-path}i, '/new-path'
r301 %r{/blog_posts}i, '/posts'
Cloudflare Feature: Bulk Redirects
- Up to 50,000 redirects per list
- O(1) lookup performance
- No expression evaluation cost
Format: CSV/JSON with source → target mappings
Category 2: Dynamic Redirect Rules
~20 redirects → Cloudflare Redirect Rules
Pattern-based redirects with capture groups:
r301 %r{/products/line/towel-warmer-(studio|sierra|barcelona)}, '/products/line/towel-warmer-classic-$1'
Cloudflare Feature: Dynamic Redirect Rules
- Uses expression language
- Supports regex-like patterns via
concat()andregex_replace() - 125 rules per zone (Business plan)
Category 3: Geo-Based Redirects
4 redirects → Cloudflare Redirect Rules with ip.geoip.country
Country-conditional redirects:
r301(/(.*)/i, '/en-CA$1', host: TLD, country: 'CA')
r301(/(.*)/i, '/en-US$1', host: TLD)
Cloudflare Feature: Redirect Rules with geo conditions
(http.host eq "warmlyyours.com" and ip.geoip.country eq "CA")
→ concat("/en-CA", http.request.uri.path)
Category 4: Host/Subdomain Redirects
14 redirects → Cloudflare Redirect Rules
Subdomain routing:
r301 %r{/(.*)}i, 'https://www.warmlyyours.com/en-US/$1', host: "scan.warmlyyours.com"
Cloudflare Feature: Redirect Rules with host matching
(http.host eq "scan.warmlyyours.com")
→ https://www.warmlyyours.com/en-US/${http.request.uri.path}
Category 5: Must Stay in Rack (Complex Logic)
~5 rules - Keep in Rails
Lambda-based logic that requires server-side processing:
r301 %r{/{2,}}, lambda { |_match, rack_env|
# Complex double-slash fixing logic
}
These include:
- Double-slash normalization (lines 32-44)
- Legacy image URL translation (lines 52-55)
- Complex locale validation
Implementation Plan
Phase 1: Preparation (Week 1)
-
Export Current Redirects
rake cloudflare:rules:export_all -
Generate Redirect Inventory
Create a rake task to parse260_redirections.rband categorize redirects:rake redirects:analyze rake redirects:export_bulk_csv rake redirects:export_dynamic_rules -
Set Up Bulk Redirect Lists
- Create
www-static-redirectslist in Cloudflare - Import CSV with static mappings
- Create
Phase 2: Geo Redirects (Week 2)
Priority: These currently check geo on every request in Rails.
-
Create Redirect Rules for TLD geo routing:
Rule 1: CA visitors on TLD → /en-CA Expression: (http.host eq "warmlyyours.com" and ip.geoip.country eq "CA" and not starts_with(http.request.uri.path, "/en-")) Target: concat("https://www.warmlyyours.com/en-CA", http.request.uri.path) Rule 2: All other TLD visitors → /en-US Expression: (http.host eq "warmlyyours.com" and not starts_with(http.request.uri.path, "/en-")) Target: concat("https://www.warmlyyours.com/en-US", http.request.uri.path) -
Deploy to Staging First
# Apply rules to warmlyyours.ws rake cloudflare:redirects:deploy[staging] -
Test with curl:
# Test CA geo (use Cloudflare header override or VPN) curl -sI "https://warmlyyours.ws/floor-heating" -H "CF-IPCountry: CA" # Expected: 301 → /en-CA/floor-heating curl -sI "https://warmlyyours.ws/floor-heating" -H "CF-IPCountry: US" # Expected: 301 → /en-US/floor-heating
Phase 3: Host Redirects (Week 2)
-
Create Redirect Rules for subdomain routing:
Rule: scan.* → www with locale Expression: (http.host contains "scan.") Target: https://www.warmlyyours.com/en-US${http.request.uri.path} -
Test scan.warmlyyours.com routing
Phase 4: Bulk Static Redirects (Week 3)
-
Generate CSV from Ruby:
# lib/tasks/redirects.rake desc "Export static redirects as CSV for Cloudflare Bulk Redirects" task export_bulk_csv: :environment do # Parse 260_redirections.rb # Filter simple path→path redirects # Output: data/cloudflare_rules/bulk_redirects.csv end -
Upload to Cloudflare:
- Dashboard: Account → Bulk Redirects → Create list
- Or via API:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/rules/lists" \ -H "Authorization: Bearer $TOKEN" \ -d '{"name":"www-static-redirects","kind":"redirect"}' -
Create Bulk Redirect Rule:
Rule: Enable www-static-redirects list Expression: (http.host eq "www.warmlyyours.com")
Phase 5: Remove Rack Redirects (Week 4)
-
Update
260_redirections.rb:- Comment out migrated redirects
- Add header noting Cloudflare handles them
- Keep only Category 5 (complex logic)
-
Verify in Production:
# Test sample redirects curl -sI https://www.warmlyyours.com/blog_posts | grep -i location # Expected: 301 to /posts (served by Cloudflare, not origin) -
Monitor for 30 days:
- Check Cloudflare analytics for redirect traffic
- Verify no 404s for migrated paths
- Compare response times
Cloudflare Rule Limits
| Plan | Redirect Rules | Bulk Redirects |
|---|---|---|
| Free | 10 | 20 |
| Pro | 25 | 500 |
| Business | 125 | 5,000 |
| Enterprise | 500+ | 50,000 |
Our Plan: Business (warmlyyours.com)
Rollback Plan
- Immediate: Disable Cloudflare Redirect Rules via Dashboard
- Rails fallback: The Rack::Rewrite rules remain in code (commented)
- Re-enable: Uncomment Rack::Rewrite rules in
260_redirections.rb
Files to Create
| File | Purpose |
|---|---|
lib/tasks/redirects.rake |
Analyze & export redirects |
data/cloudflare_rules/bulk_redirects.csv |
Static redirect mappings |
data/cloudflare_rules/redirect_rules.yml |
Dynamic rule definitions |
app/services/cloudflare_redirect_service.rb |
API for managing redirects |
Testing Strategy
Before Migration
# Capture current behavior
curl -sI https://www.warmlyyours.com/blog_posts > before_blog.txt
curl -sI https://www.warmlyyours.com/towel-warmers > before_towel.txt
# ... for each major redirect category
After Migration
# Compare responses
diff before_blog.txt after_blog.txt
# Verify same redirect target, but via Cloudflare
Headers to Check
CF-Ray- Confirms Cloudflare processed requestX-Redirect-By: Cloudflare- (if we add custom header)Server: cloudflare- Cloudflare served response
Metrics to Track
- Redirect Latency - Should drop from ~200ms to ~10ms
- Origin Requests - Should decrease for redirect paths
- Error Rate - Monitor for unexpected 404s
- Cache Hit Ratio - Redirects should be cached