Web Development 5-8 minutes

Laravel 13 Page Caching: Cut Your Blog TTFB Below 200 ms

Diego Cortés
Diego Cortés
Full Stack Developer & SEO Specialist
Share:
Laravel 13 Page Caching: Cut Your Blog TTFB Below 200 ms
Image generated with AI

A Laravel blog without page caching answers in 800 ms+ of TTFB; with the right setup it drops below 200 ms without touching your business logic. This guide walks you through the full combo: response caching, Nginx FastCGI, config cache, and Octane.

What TTFB Is and Why It Matters in 2026

Time To First Byte (TTFB) measures the time between the browser's request and the first byte of the server's response. It is the first signal Google sees about your site's speed: if the server is slow, everything else (LCP, INP) starts at a disadvantage.

Google's Core Web Vitals Thresholds

Core Web Vitals evaluates TTFB at the 75th percentile of real users. Up to 800 ms is considered "good", 800-1800 ms is "needs improvement", and anything above 1800 ms is "poor". The detail most people miss: 800 ms is the floor, not the goal. To keep LCP comfortable on mobile, aim for under 200 ms.

Why a Blog Is the Perfect Candidate for Caching

A blog has the ideal caching profile: GET routes, public pages, few changes, and no per-user state. The HTML of an article is identical for every visitor, so regenerating it on each request is pure waste. That is exactly where full-page caching delivers the most with the least effort.

Measure First: Diagnose Before You Optimize

Never optimize blind. Measure the real TTFB of your most visited routes and record the values before changing anything.

curl -o /dev/null -s -w "ttfb=%{time_starttransfer}s total=%{time_total}s\n" https://your-blog.com/article

Run the request several times and keep the p75, not the best case. Chrome DevTools (Network tab) and PageSpeed Insights show the same metric from the browser's perspective. Once you have the number, you know whether the bottleneck is the server, the database, or the application bootstrap.

Full-Page Caching with spatie/laravel-responsecache

The Spatie package is the fastest way to cache entire pages in Laravel: the first request runs the controller and stores the HTML; subsequent requests are served from cache without touching your logic.

Installation and Basic Setup

composer require spatie/laravel-responsecache
php artisan vendor:publish --provider="Spatie\ResponseCache\ResponseCacheServiceProvider"

By default it caches GET responses with a 200 status. In the published config you can exclude routes, limit by URI, or set the TTL. For a blog, the default 24-hour lifetime is a solid starting point.

Invalidating the Cache When You Publish

The critical part: when you publish or edit an article, you must invalidate the cache so visitors see the change. The package ships helpers, and you can also call it from your publishing code:

use Spatie\ResponseCache\Facades\ResponseCache;

ResponseCache::clear(); // invalidates the whole response cache

If you publish from an admin panel or an artisan command, call ResponseCache::clear() right after saving the post. Alternatively, cache only what changes rarely and keep the homepage out of it.

Flexible Caching in v8

Version 8 added "flexible caching": rules per URI and headers that let you decide what gets cached and for how long. For example, you can cache articles with a long TTL and listing pages with a short one, or exclude requests based on headers you do not want to store.

The Server-Side Alternative: Nginx FastCGI Cache

If you prefer to avoid dependencies or want an extra layer, Nginx can cache the full response at the server level. The fastcgi_cache directive stores the HTML and serves it without even starting PHP:

fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=laravel:10m max_size=1g inactive=60m;

location ~ \.php$ {
    fastcgi_cache laravel;
    fastcgi_cache_valid 200 60m;
    fastcgi_cache_bypass $http_authorization;
    add_header X-FastCGI-Cache $upstream_cache_status;
}

With the X-FastCGI-Cache header you can see in the response whether it came from cache (HIT) or was generated (MISS). It is a solid option when you do not want the cache living inside the application.

The Rest of the Combo: Config Cache, Queries, and Octane

Page caching attacks the biggest cost, but it is not the only piece. Laravel's bootstrap and heavy queries also add milliseconds.

php artisan optimize (config, route, view, event)

php artisan optimize

This command compiles configuration, routes, views, and events into cached files. In production it stops Laravel from reading and parsing every file on each request. It is a one-line change that usually shaves tens of milliseconds off TTFB.

Cache::remember for Heavy Queries

Queries that repeat on every visit (menus, related articles, counters) deserve fragment caching:

use Illuminate\Support\Facades\Cache;

$posts = Cache::remember('latest_posts', 3600, function () {
    return Post::published()->latest()->take(10)->get();
});

The file store works from day one; if the site scales, switch to Redis by only touching config/cache.php.

Laravel Octane: No Per-Request Bootstrap

Octane (with Swoole, RoadRunner, or FrankenPHP) keeps the application in memory with long-lived workers. Laravel's bootstrap — which runs on every request under PHP-FPM — happens only once, removing the biggest TTFB hit after page caching.

php artisan octane:start --server=frankenphp --port=8000

Combine Octane with response caching and you get the ideal scenario: cached pages never even reach PHP, and generated pages run without a cold start.

Expected Results and How to Measure Them

With the full combo, a blog that answered in about 900 ms of TTFB usually drops to the 180-250 ms range. Exact numbers depend on hosting, but the order of magnitude repeats in practice. Run the same curl -w command as before and compare: if TTFB dropped and LCP in PageSpeed Insights improved, the optimization worked.

Conclusion

Page caching in Laravel 13 is the most cost-effective tool to speed up a blog: install responsecache, invalidate on publish, add Nginx FastCGI if you want an extra layer, and finish with optimize, Cache::remember, and Octane. Measure before and after, and a TTFB below 200 ms stops being a goal and becomes your new normal. Keep reading the blog for more Laravel and web performance guides.

Categories