Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Laravel Caching Strategies: Config, Route, View, and Data

By Kokil Thapa | Last reviewed: September 2026

Laravel caching strategies: config, route, view, and data are the four layers that separate a fast production app from one that re-parses PHP files on every request. Laravel 13 runs on PHP 8.3 or higher; Laravel 12 still works on PHP 8.2 and remains supported through February 2027. Most teams I work with already use Redis for Laravel application caching, yet they skip route and config caching entirely. That gap shows up as slow cold starts after deploy and unnecessary CPU use under traffic spikes.

What Are Laravel Caching Strategies for Config, Route, View, and Data?

Think of Laravel boot as four separate cost centres. Config caching removes repeated parsing of every file in config/. Route caching pre-compiles your route table so the router skips regex matching work. View caching compiles Blade templates to plain PHP ahead of time. Data caching stores expensive query or API results outside the request cycle.

These layers stack. They do not replace each other. A site with perfect Redis data caching still benefits from config and route caches because those run before your controller ever executes. On a legal-tech portal I maintain, skipping route cache added roughly 80–120 ms to every request on a modest VPS. After enabling it, TTFB dropped enough that Core Web Vitals stopped flagging server response time.

Laravel Request Boot LayersHTTP Requestindex.phpConfig Cachebootstrap/cacheRoute Cacheroutes-v7.phpMiddlewareKernel stackView CacheCompiled BladeControllerBusiness logicData CacheRedis / DBProduction goal: cache static boot cost once, invalidate on deploySee also: improving web performance with caching strategies
Four Laravel caching layers—config and route caches run at boot; view and data caches cut render and query cost per request.

Your .env file is read once when config is cached. That is why you must never call env() outside config files in production. Laravel documents this explicitly, and breaking it is one of the most common post-deploy bugs I see on client projects.

Config cache mechanics

Running php artisan config:cache merges all config files into a single bootstrap file at bootstrap/cache/config.php. Reads become a single include instead of dozens of file operations. The trade-off is that config changes require re-running the command or clearing the cache.

Route cache mechanics

php artisan route:cache serialises the route collection. Closures cannot be cached—only controller and invokable routes work. If your routes file uses inline closures for quick tests, move them to controllers before caching.

How Do You Cache Laravel Config and Routes in Production?

Production deploy scripts should treat cache warming as a required step, not an optional optimisation. I use Deployer 7 on several sister sites sharing GitLab CI, and the deploy recipe always runs cache commands after the symlink swap and PHP-FPM reload. The order matters because stale opcache can serve old bootstrap files if you cache before swapping releases.

Production Deploy Cache PipelineGit PullNew releaseComposer--no-devSymlinkSwap currentFPM ReloadOpcache flushArtisan Cache Warm (run in new release path)config:cacheroute:cacheview:cacheDo NOT cache duringlocal developmentClear on rollbackconfig:clear route:clear
Warm Laravel config, route, and view caches after symlink swap and PHP-FPM reload so opcache serves the new bootstrap files.

A typical Deployer task block looks like this:

task('artisan:optimize', function () {
    run('{{bin/php}} {{release_path}}/artisan config:cache');
    run('{{bin/php}} {{release_path}}/artisan route:cache');
    run('{{bin/php}} {{release_path}}/artisan view:cache');
    run('{{bin/php}} {{release_path}}/artisan event:cache');
})->once();

Run these only in production. During local development, cached config hides .env changes until you run php artisan config:clear. I have lost hours to that mistake. Keep a shell alias if it helps.

For teams using testing and optimisation services, the first audit item is usually whether production actually runs these commands. Many hosting panels deploy code but skip Artisan entirely.

Environment-specific config

Set APP_ENV=production and APP_DEBUG=false before caching config. Debug mode disables some optimisations and exposes stack traces. Your config/cache.php default store should point to Redis in production:

CACHE_STORE=redis
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Redis 8.10 is the current anchor version. On budget VPS hosts in Nepal, a single Redis instance on the same server costs nothing extra beyond RAM. Expect roughly Rs 0 if Redis is already bundled, versus Rs 1,500–3,000/month (~USD 11–22) for a managed Redis add-on from cloud providers.

When Should You Use View Caching vs Response Caching in Laravel?

View caching compiles Blade to PHP files under storage/framework/views. Response caching stores the entire HTTP output—headers and body—for a given URL. They solve different problems.

Use php artisan view:cache on every production deploy. It removes per-request Blade compilation cost. That matters on pages with deep partial includes, like eCommerce category trees or legal service directories with shared sidebars.

Response caching—via packages like spatie/laravel-responsecache or middleware you write yourself—fits mostly static pages: blog posts, landing pages, documentation. It does not fit authenticated dashboards, carts, or anything with CSRF tokens in the HTML.

LayerCommand / APIBest forInvalidationGotcha
Config cacheconfig:cacheAll production appsRe-run on deployenv() outside config breaks
Route cacheroute:cacheApps with 50+ routesRe-run on deployClosures cannot cache
View cacheview:cacheBlade-heavy UIsRe-run on deployDoes not cache data
Data cacheCache::remember()Expensive queries, API callsTTL or event-drivenStale reads if TTL too long
Response cacheMiddleware / packageAnonymous HTML pagesTag flush or TTLCSRF and session bleed

On a WooCommerce florist site in the portfolio, view caching helped, but product stock still needed data-layer invalidation. Caching the HTML would have shown wrong availability. Match the cache type to what actually changes.

Read improving web performance with caching strategies for browser and CDN layers that complement Laravel-side work. Server caching alone does not fix unoptimised assets or missing compression.

How Do You Cache Application Data Safely in Laravel?

Data caching is where most performance wins live after boot optimisations are in place. The pattern is simple: wrap expensive work in Cache::remember(), set a TTL, and invalidate when underlying data changes.

Cache::remember() Decision FlowControllerNeeds dataCache KeyExists?Cache HITReturn valueCache MISSRun queryMySQLPostgreSQLStore in RedisSet TTLInvalidate on model update: Cache::forget() or cache tagsPair with database query caching strategies for heavy reports
Laravel data caching with Cache::remember()—check Redis first, fall back to the database, then store with a TTL and invalidate on writes.

Basic remember pattern

use Illuminate\Support\Facades\Cache;

$categories = Cache::remember('shop.categories', 3600, function () {
    return Category::with('children')
        ->whereNull('parent_id')
        ->orderBy('sort_order')
        ->get();
});

Use descriptive, namespaced keys. Prefix by tenant or locale when you serve multiple sites from one codebase: tenant:42:shop.categories. Collisions across environments are another common bug—include APP_NAME or environment in the key when Redis is shared.

Cache tags and invalidation

Redis supports cache tags in Laravel when your store driver allows it. Tag related keys and flush the group on update:

Cache::tags(['products'])->put("product.{$id}", $product, 3600);

/* On product update */
Cache::tags(['products'])->flush();

File and database cache drivers do not support tags efficiently. If you are still on the file driver, migrate to Redis before building tag-based invalidation. See database query caching strategies for overlap between ORM-level and application-level caching.

Eloquent and query caching

Laravel does not cache Eloquent results automatically. You must opt in. For read-heavy reporting, consider rememberForever() with explicit invalidation on writes rather than a short TTL that hammers the database on expiry.

Watch for N+1 queries inside cached closures. Caching a collection that lazy-loads relations on access still triggers extra queries when the view iterates. Eager load inside the closure. The Laravel N+1 detection guide covers tooling like Debugbar and Telescope for catching this during development.

On booking systems like Adventure Third Pole Trek, I cache itinerary lists and destination filters but never cache user-specific booking state. Personalised data belongs in the session or a short-lived cache keyed by user ID with tight TTL.

Which Cache Driver Should You Choose for Laravel in 2026?

Your config/cache.php default store drives every Cache facade call. Pick based on infrastructure, not blog benchmarks.

Cache Driver Selection GuideFileDev / single serverNo tags at scaleDatabaseShared hostingAdds DB loadRedisProduction defaultTags + queuesMemcachedPure cache RAMNo persistenceRecommended stack for Laravel 13 productionRedis 8.10 — cache + session + queue on one instanceHigh traffic?Separate Redis DB indexMulti-server?Central Redis required
Laravel cache driver comparison—Redis is the production default for config-route-view-data strategies that need tags, TTL, and shared state.
  • Redis: Default choice for production. Supports tags, pub/sub, queues, and sessions on one service. Use separate Redis databases (REDIS_CACHE_DB=1) to isolate cache from queues.
  • Memcached 1.6.x: Fast pure cache when you already run it and do not need persistence or Laravel queue integration.
  • Database: Works on shared hosting with only MySQL available. Creates a cache table via migration. Slower than Redis but better than no cache.
  • File: Fine for local dev and single-server hobby projects. Poor choice for horizontal scaling—each app server holds its own copy.

For PostgreSQL-backed apps, read PostgreSQL for Laravel developers alongside caching—index tuning and cache layers solve different bottlenecks. MySQL 9.7 and MariaDB 12.3 remain common on Nepali shared hosts; Redis still sits beside them as the cache tier.

Official reference: the Laravel 12.x cache documentation covers drivers, tags, and atomic locks. Laravel 13 docs follow the same API with minor config additions.

How Do You Invalidate Laravel Caches After Deployment?

Stale cache is worse than no cache. Users see old prices, outdated legal fees, or wrong appointment slots. Build invalidation into your deploy and your domain events.

  1. Boot caches: Re-run config:cache, route:cache, and view:cache on every deploy. Never rely on the previous release's bootstrap files.
  2. Data caches: Flush selectively with tags, or bump a version prefix in keys (v3:products.list) when schema changes make old serialised objects invalid.
  3. Opcache: Reload PHP-FPM after symlink swap. Without reload, workers may serve old compiled PHP including cached config files.
  4. CDN / edge: Purge edge cache when response caching sits behind Cloudflare or similar. Application cache and CDN cache are independent layers.
  5. Queues: Restart queue workers after deploy so they load new code. php artisan queue:restart signals graceful restart.

On rollback, clear boot caches before restoring the previous release:

php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear

Use cache:clear carefully in production—it wipes all data cache keys. Prefer tag flushes or targeted Cache::forget() calls. For high-traffic patterns, read caching strategies for high traffic sites.

Scheduled tasks that rebuild cache proactively can smooth traffic spikes. Warm popular keys after deploy:

/* app/Console/Kernel.php or routes/console.php */
Schedule::command('cache:warm-homepage')->hourly();

I register a custom Artisan command that hits internal endpoints or calls service classes directly to populate Redis before peak hours. On Nepal-facing sites, that often means warming caches before 10 AM NPT when traffic rises.

Atomic locks for cache stampede

When a popular key expires, many requests may hit the database at once. Laravel's cache lock prevents that:

$lock = Cache::lock('compute:featured-products', 10);

if ($lock->get()) {
    try {
        $products = Product::featured()->get();
        Cache::put('featured-products', $products, 3600);
    } finally {
        $lock->release();
    }
}

The Redis distributed locks pattern explains the underlying concept. Laravel abstracts it, but you still need sensible lock TTLs.

Testing cache behaviour

In tests, use the array cache driver via phpunit.xml:

<env name="CACHE_STORE" value="array"/>

Feature tests should assert that updating a model clears or refreshes the relevant cache key. Cache bugs are silent until production traffic exposes them. Pair this with Laravel feature testing best practices for CI pipelines that run on GitLab CI or GitHub Actions.

If you need to inspect serialised cache payloads during debugging, paste JSON fragments into the JSON formatter tool to readable structure—faster than dumping raw Redis strings in tinker.

Local development workflow

Never run config:cache locally unless you are reproducing a production bug. Standard local workflow:

php artisan optimize:clear

That single command clears config, route, view, event, and compiled caches. Run it after pulling branch changes that touch routes or config.

For API-heavy apps, combine data caching with sensible HTTP cache headers on read-only endpoints. The Laravel API best practices guide covers ETag and Cache-Control headers that reduce origin load even when Redis is warm.

Enterprise apps with complex modules may centralise cache key constants in a dedicated class or service provider. See Laravel service providers explained for registering a singleton that wraps cache access with your naming convention.

When performance still falls short after all four layers are tuned, speed optimisation work usually shifts to asset delivery, database indexes, and CDN configuration—not more aggressive TTLs that serve stale business data.

Key Takeaways

  • Run config:cache, route:cache, and view:cache on every production deploy after symlink swap and PHP-FPM reload.
  • Never call env() outside config files once config is cached—use config('app.key') instead.
  • Use Redis 8.10 as the default data cache store; separate DB indexes for cache, session, and queue.
  • Wrap expensive queries in Cache::remember() with namespaced keys and tag-based invalidation on writes.
  • Match cache type to content: view cache for Blade, data cache for queries, response cache only for anonymous static HTML.
  • Clear boot caches on rollback and restart queue workers so no process serves stale compiled bootstrap files.

People Also Ask

Does Laravel route caching work with closure routes?

No. Laravel route caching serialises named routes to controllers and invokable classes only. Closure-based routes cause an exception when you run route:cache. Move closure logic to controller methods before enabling route cache in production.

What is the difference between config:cache and optimize?

php artisan optimize runs config, event, route, and view caching together in Laravel 12+. Individual commands still exist when you need granular control during debugging. Use optimize:clear to wipe all compiled bootstrap files at once.

Can I use the same Redis instance for cache and sessions?

Yes, but use different Redis database numbers via REDIS_CACHE_DB and REDIS_SESSION_DB in .env. That prevents cache:clear from flushing active user sessions and makes monitoring easier.

How long should I set cache TTL for database queries?

Match TTL to business tolerance for stale data. Product catalogues often use 15–60 minutes. User permissions might use 5 minutes or event-driven invalidation. Financial or inventory-critical data should use short TTLs plus explicit flush on update, not long rememberForever() calls.

Ship Faster Laravel Apps With the Right Cache Layers

Laravel caching strategies: config, route, view, and data are not optional extras for production—they are baseline infrastructure. Config and route caches cut boot time on every request. View cache removes Blade compilation overhead. Data cache in Redis protects your MySQL or PostgreSQL tier under load. Wire all four into your deploy script, invalidate deliberately, and measure TTFB before and after.

If you want help auditing cache configuration on an existing app or building deploy automation from scratch, review the Court Marriage In Nepal and other portfolio projects for production Laravel work, or reach out via contact us to discuss your stack. For ongoing performance work after launch, support and maintenance keeps cache strategies current as Laravel and PHP versions evolve.

Frequently Asked Questions

Four stacked layers: config:cache merges config files at boot, route:cache pre-compiles routes, view:cache compiles Blade ahead of time, and Cache::remember() stores query or API results in Redis. Each targets a different cost centre and they do not replace each other.

Run php artisan config:cache and route:cache after every deploy, ideally after the symlink swap and PHP-FPM reload so opcache serves fresh bootstrap files. On sites I maintain with Deployer 7 and GitLab CI, these commands sit in a dedicated artisan:optimize task alongside view:cache and event:cache. Set APP_ENV=production and APP_DEBUG=false first. During local development, skip config:cache because it hides .env changes until you run config:clear.

When you run config:cache, Laravel reads .env once and writes merged values into bootstrap/cache/config.php. After that, env() returns null outside config files because the environment loader is bypassed. Use config('app.key') instead. Breaking this rule is one of the most common post-deploy bugs I see on client projects, causing missing API keys and broken integrations that work fine locally.

No. php artisan route:cache only serialises controller and invokable routes. Inline closures in routes/web.php cannot be cached and will cause the command to fail or produce incomplete route tables. Move closure logic into dedicated controller methods before enabling route cache. This matters once your app grows past roughly 50 routes, where uncached regex matching adds measurable boot overhead on every request.

Use php artisan view:cache on every production deploy to pre-compile Blade templates under storage/framework/views. That removes per-request compilation cost on pages with deep partial includes. Response caching via spatie/laravel-responsecache or custom middleware stores the full HTTP output and suits mostly static anonymous pages like blog posts. It does not fit authenticated dashboards, carts, or pages with CSRF tokens. Match the cache type to what actually changes.

Wrap expensive queries or API calls in Cache::remember() with a descriptive namespaced key and TTL. Prefix keys by tenant or locale when one codebase serves multiple sites, and include APP_NAME when Redis is shared across environments. Eager load relations inside the closure to avoid N+1 queries when the view iterates cached collections. Invalidate on writes using cache tags when on Redis, or bump a version prefix in keys after schema changes.

Redis 8.10 is the production default. It supports tags, pub/sub, queues, and sessions on one service. Use REDIS_CACHE_DB=1 to isolate cache from queues. Memcached 1.6.x works when you already run it and do not need persistence or queue integration. The database driver suits shared hosting with only MySQL 9.7 or MariaDB 12.3 available. File is fine for local dev but poor for horizontal scaling because each server holds its own copy.

Rs 0 if Redis is already bundled on your VPS. Managed Redis add-ons run roughly Rs 1,500–3,000/month (~USD 11–22).

Re-run config:cache, route:cache, and view:cache on every deploy rather than relying on the previous release bootstrap files. Reload PHP-FPM after symlink swap so workers do not serve old opcache entries. Restart queue workers with php artisan queue:restart. For data caches, flush selectively with tags or targeted Cache::forget() rather than cache:clear, which wipes all keys. On rollback, run config:clear, route:clear, view:clear, and cache:clear before restoring the previous release.

Config cache runs at application boot before any controller executes. It merges every file in config/ into a single bootstrap/cache/config.php include, eliminating repeated file parsing. Data cache runs during request handling via Cache::remember() and stores query or API results in Redis with a TTL. A site with perfect Redis data caching still benefits from config and route caches because those boot costs happen before your controller code runs.

Almost never during normal development. Cached config hides .env changes until you run config:clear, and I have lost hours to that mistake. Use php artisan optimize:clear after pulling branch changes that touch routes or config. Run config:cache locally only when reproducing a specific production bug related to cached bootstrap files.

No, not efficiently. Cache tags require Redis or another driver that supports tag-based grouping. File and database drivers lack reliable tag flush behaviour. If you plan tag-based invalidation such as Cache::tags(['products'])->flush() on product updates, migrate to Redis before building that logic. Otherwise stale tagged keys persist silently until TTL expiry.

When a popular key expires, many concurrent requests can hit the database simultaneously. Laravel provides atomic cache locks via Cache::lock(). Acquire the lock, compute the expensive result once, store it with Cache::put(), and release in a finally block. Set a sensible lock TTL so a crashed worker does not block regeneration indefinitely. This pattern matters on high-traffic pages like featured product lists or homepage category trees.

No for personalised state. On booking systems I have worked on, itinerary lists and destination filters cache well because they are shared across users. Individual booking state belongs in the session or a short-lived cache keyed by user ID with a tight TTL. Caching authenticated dashboard HTML via response cache causes CSRF and session bleed. Never cache data that must reflect real-time user actions or permissions.

Set CACHE_STORE=array in phpunit.xml so tests never touch production Redis. Write feature tests that assert updating a model clears or refreshes the relevant cache key. Cache bugs are silent until production traffic exposes them, so pairing cache assertions with GitLab CI or GitHub Actions pipelines catches invalidation gaps before deploy. Use optimize:clear locally, not config:cache, to keep the test and dev workflow aligned with uncached bootstrap behaviour.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: