DEV Community

ahmet gedik
ahmet gedik

Posted on

Implementing Cloudflare Page Cache with Custom Rules

Cloudflare sits in front of TrendVidStream, providing SSL, DDoS protection, and page caching. Here's how to configure Cloudflare page caching for a dynamic PHP site with custom cache rules.

The Challenge

Cloudflare does not cache HTML by default. For a PHP site that generates pages dynamically but whose content changes infrequently, we need to tell Cloudflare to cache specific page types with appropriate TTLs.

SSL Configuration: Flexible Mode

All TrendVidStream sites use Cloudflare Flexible SSL:

User ──(HTTPS)──> Cloudflare ──(HTTP)──> Origin Server
Enter fullscreen mode Exit fullscreen mode

Important: Flexible mode means no SSL certificate is needed on the origin. But it creates a redirect loop if your .htaccess unconditionally redirects HTTP to HTTPS:

# WRONG - causes redirect loop with Cloudflare Flexible
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# CORRECT - check Cloudflare's header
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
Enter fullscreen mode Exit fullscreen mode

Page Rules for Caching

Rule 1: Cache Homepage
URL: trendvidstream.com/
Cache Level: Cache Everything
Edge Cache TTL: 3 hours
Browser Cache TTL: 1 hour

Rule 2: Cache Category Pages
URL: trendvidstream.com/category/*
Cache Level: Cache Everything
Edge Cache TTL: 3 hours
Browser Cache TTL: 1 hour

Rule 3: Cache Watch Pages
URL: trendvidstream.com/watch/*
Cache Level: Cache Everything
Edge Cache TTL: 6 hours
Browser Cache TTL: 2 hours

Rule 4: Bypass Cache for Admin
URL: trendvidstream.com/admin/*
Cache Level: Bypass
Enter fullscreen mode Exit fullscreen mode

PHP Cache Headers

Cloudflare respects Cache-Control headers from the origin:

<?php

class ResponseHeaders
{
    public static function forPage(string $type): void
    {
        $config = match($type) {
            'home' => [
                'max-age' => 10800,
                'swr' => 7200,
                's-maxage' => 10800,
            ],
            'category' => [
                'max-age' => 10800,
                'swr' => 7200,
                's-maxage' => 10800,
            ],
            'watch' => [
                'max-age' => 21600,
                'swr' => 86400,
                's-maxage' => 21600,
            ],
            'search' => [
                'max-age' => 600,
                'swr' => 1800,
                's-maxage' => 600,
            ],
            default => [
                'max-age' => 3600,
                'swr' => 3600,
                's-maxage' => 3600,
            ],
        };

        header(sprintf(
            'Cache-Control: public, max-age=%d, s-maxage=%d, stale-while-revalidate=%d',
            $config['max-age'],
            $config['s-maxage'],
            $config['swr']
        ));

        header('Vary: Accept-Encoding');
    }
}
Enter fullscreen mode Exit fullscreen mode

Cache Purging After Deploy

<?php

class CloudflareCache
{
    private string $zoneId;
    private string $apiToken;

    public function __construct(string $zoneId, string $apiToken)
    {
        $this->zoneId = $zoneId;
        $this->apiToken = $apiToken;
    }

    public function purgeAll(): bool
    {
        $ch = curl_init("https://api.cloudflare.com/client/v4/zones/{$this->zoneId}/purge_cache");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode(['purge_everything' => true]),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer {$this->apiToken}",
                'Content-Type: application/json',
            ],
        ]);

        $response = json_decode(curl_exec($ch), true);
        curl_close($ch);

        return $response['success'] ?? false;
    }

    public function purgeUrls(array $urls): bool
    {
        $ch = curl_init("https://api.cloudflare.com/client/v4/zones/{$this->zoneId}/purge_cache");
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode(['files' => $urls]),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer {$this->apiToken}",
                'Content-Type: application/json',
            ],
        ]);

        $response = json_decode(curl_exec($ch), true);
        curl_close($ch);

        return $response['success'] ?? false;
    }
}
Enter fullscreen mode Exit fullscreen mode

Cloudflare Settings to Watch

// Settings that can interfere with caching:

// 1. Block AI Scrapers: OFF
// This injects content into robots.txt that GSC flags as errors

// 2. Display Content Signals Policy: OFF
// Adds "Content-Signal" to robots.txt, GSC flags it

// 3. Auto Minify: Test carefully
// Can break inline scripts if not properly handled

// 4. Rocket Loader: OFF for video sites
// Delays JavaScript execution, can break video players
Enter fullscreen mode Exit fullscreen mode

This caching configuration at TrendVidStream ensures most requests are served from Cloudflare's edge, reducing origin server load significantly.

The result is fast page loads worldwide while maintaining fresh content through appropriate cache TTLs.

Top comments (0)