Prerequisites: A Cloudflare account with your domain's DNS managed by Cloudflare. Free plan covers most of these settings; some (Argo, Advanced Cache Rules, Early Hints in all cases) require a Pro plan or higher. We note plan requirements where relevant.

Why Cloudflare Changes Everything for Laravel Performance

A Laravel application running on a single server in Frankfurt will have 180–220ms latency for users in Dubai. The same application behind Cloudflare, with correct caching configured, serves cached responses from Cloudflare's Dubai edge node — 8–12ms. For real users, that's the difference between a site that feels instant and one that feels sluggish.

But Cloudflare misconfiguration is common. The most frequent mistake we see: developers set up Cloudflare in proxy mode (the orange cloud) but don't configure any cache rules, so nothing except static assets gets cached. The Laravel app still handles every request. You get DDoS protection and SSL, but none of the performance benefit.

This guide fixes that completely.

Part 1: Foundation Settings (Apply First)

SSL/TLS Configuration

In Cloudflare Dashboard → SSL/TLS → Overview: set encryption mode to Full (strict). Never use "Flexible" on a production Laravel app — it allows unencrypted connections between Cloudflare and your origin, negating the security benefit.

Enable these additional SSL settings:

  • Always Use HTTPS: ON
  • HTTP Strict Transport Security (HSTS): Enable, max-age 6 months minimum, includeSubDomains ON
  • Minimum TLS Version: TLS 1.2
  • TLS 1.3: ON
  • Opportunistic Encryption: ON

Speed Settings

In Cloudflare Dashboard → Speed → Optimization:

  • Auto Minify: Enable HTML, CSS, and JavaScript
  • Brotli: ON — this typically reduces response sizes 20–26% more than gzip
  • Early Hints: ON (Pro plan) — pre-pushes critical resources before the HTML response completes, improving LCP
  • HTTP/3 (QUIC): ON — reduces connection overhead, especially important on mobile networks in GCC
  • 0-RTT Connection Resumption: ON
  • Rocket Loader: OFF for Laravel apps — it defers JS execution and breaks Alpine.js and similar frameworks that initialize on DOM ready

Network Settings

  • HTTP/2: ON
  • gRPC: ON (if your app uses it)
  • WebSockets: ON (if your app uses real-time features)
  • IP Geolocation: ON — adds CF-IPCountry header, useful for locale detection in Laravel
  • True Client IP Header: ON

Part 2: Cache Configuration — The Critical Part

Default Cache Behavior

Cloudflare's default behavior only caches files with known static extensions (CSS, JS, images, fonts). HTML pages — your Laravel routes — are not cached by default. To cache HTML, you must explicitly configure it. Here's how.

Cache Rules (New Rules System — Use This, Not Page Rules)

Navigate to Caching → Cache Rules → Create Rule. Set up these rules in priority order (Rule 1 takes highest priority):

Rule 1: Bypass Cache for Authenticated & Admin Routes

When:
  URI Path contains "/admin" OR
  URI Path contains "/dashboard" OR
  URI Path contains "/api" OR
  URI Path starts with "/login" OR
  URI Path starts with "/register" OR
  URI Path starts with "/logout" OR
  Cookie contains "laravel_session"

Then:
  Cache Status: Bypass

Rule 2: Cache Static Assets (Long TTL)

When:
  URI Path ends with ".css" OR
  URI Path ends with ".js" OR
  URI Path ends with ".webp" OR
  URI Path ends with ".avif" OR
  URI Path ends with ".png" OR
  URI Path ends with ".jpg" OR
  URI Path ends with ".svg" OR
  URI Path ends with ".woff2" OR
  URI Path ends with ".woff"

Then:
  Cache Everything
  Edge Cache TTL: 1 month
  Browser Cache TTL: 7 days

Rule 3: Cache Public Pages — Homepage

When:
  URI Path equals "/" OR
  URI Path equals "/en/" OR
  URI Path equals "/ar/"

Then:
  Cache Everything
  Edge Cache TTL: 1 hour
  Browser Cache TTL: 5 minutes

Rule 4: Cache Service Pages (Long)

When:
  URI Path starts with "/en/services" OR
  URI Path starts with "/ar/services" OR
  URI Path starts with "/en/portfolio" OR
  URI Path starts with "/ar/portfolio"

Then:
  Cache Everything
  Edge Cache TTL: 24 hours
  Browser Cache TTL: 1 hour

Rule 5: Cache Blog Articles (Medium)

When:
  URI Path starts with "/en/blog" OR
  URI Path starts with "/ar/blog"

Then:
  Cache Everything
  Edge Cache TTL: 12 hours
  Browser Cache TTL: 30 minutes

Laravel-Side Cache Headers

For Cloudflare's "Cache Everything" rules to work, your Laravel responses must include proper Cache-Control headers. Add this middleware to your public routes:

// App\Http\Middleware\SetCacheHeaders.php
public function handle(Request $request, Closure $next, string $maxAge = '3600'): Response
{
    $response = $next($request);

    if ($request->isMethodSafe() && !$request->user()) {
        $response->headers->set(
            'Cache-Control',
            "public, max-age={$maxAge}, s-maxage=" . ($maxAge * 4)
        );
        $response->headers->set('Vary', 'Accept-Language, Accept-Encoding');
    }

    return $response;
}

// Usage in routes/web.php:
Route::get('/', HomeController::class)
    ->middleware('cache.headers:3600');  // 1 hour browser, 4 hours edge

Purging the Cache on Deployments

Add a Cloudflare cache purge step to your deployment pipeline. Using the Cloudflare API:

# In your deploy script (GitHub Actions, Envoyer, etc.)
curl -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/purge_cache" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything":true}'

# For surgical purges (better for high-traffic sites):
--data '{"files":["https://yourdomain.com/","https://yourdomain.com/en/","https://yourdomain.com/ar/"]}'

Part 3: Security Rules (WAF)

Managed Rulesets

In Security → WAF → Managed Rules, enable:

  • Cloudflare Managed Ruleset: ON (Medium sensitivity)
  • Cloudflare OWASP Core Ruleset: ON (Medium + High sensitivity, Paranoia Level 2)
  • Cloudflare PHP Ruleset: ON — specifically protects PHP/Laravel apps

Custom WAF Rules for Laravel

Rule: Block malicious bot patterns:

When:
  http.user_agent contains "sqlmap" OR
  http.user_agent contains "nikto" OR
  http.user_agent contains "acunetix" OR
  http.request.uri.path contains "wp-admin" OR
  http.request.uri.path contains "wp-login" OR
  http.request.uri.path contains ".php" AND
  NOT http.request.uri.path contains "/index.php"

Action: Block

Rule: Rate limit login endpoint:

When:
  URI Path equals "/login" AND
  Method: POST

Rate: 5 requests per 1 minute per IP
Action: Block for 10 minutes

Part 4: Performance for GCC/Middle East Audiences

Argo Smart Routing (Pro Plan)

Argo routes traffic through Cloudflare's private backbone rather than the public internet. For origin servers in Europe serving users in the GCC, this typically reduces latency by 30–35%. Enable it in Traffic → Argo. At roughly $5/month for most sites, the ROI is immediate in terms of user experience and conversions.

Tiered Caching

Enable Tiered Cache in Caching → Tiered Cache → Smart Tiered Caching. This allows Cloudflare edge nodes to serve cache hits from each other rather than always hitting your origin. When a user in Riyadh requests a page already cached in Dubai's edge node, they get served from Dubai — not your Frankfurt server.

Polish (Image Optimization)

In Speed → Optimization → Polish: enable Lossless or Lossy compression (test both and measure). This automatically converts images to WebP for supporting browsers and optimizes JPEG/PNG compression — without any code changes to your Laravel app. For sites with many user-uploaded images, this alone can reduce page weight by 40%.

Part 5: Common Mistakes and How to Avoid Them

Mistake 1: Caching authenticated pages. If a logged-in user's page gets cached and served to an anonymous visitor, you've just leaked private data. Always use the session cookie bypass rule first in your rule priority order.

Mistake 2: Not setting Vary headers. If your site serves different content based on Accept-Language (which all multilingual Laravel apps do), you must include Vary: Accept-Language in your responses. Without it, Cloudflare serves the same cached response regardless of the user's language.

Mistake 3: Forgetting CSRF tokens in cached HTML. If you cache HTML pages that include CSRF tokens in meta tags, the cached HTML will have stale tokens. Move CSRF token fetching to JavaScript (a quick AJAX call to a non-cached endpoint) rather than embedding in the HTML of cached pages.

Mistake 4: Cloudflare caching 500 errors. Add a Cache Rule with condition "Response Status Code equals 500, 503" and action "Bypass Cache". By default Cloudflare may cache error responses for a short period — for a production Laravel app, you want errors to hit the origin on every request so your fixes take effect immediately.

Want Us to Set This Up For Your App?

We configure and maintain Cloudflare for all the Laravel applications we build and manage. If you want this exact setup applied to your production app — with testing to confirm it's working correctly — get in touch.