- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Laravel Caching Strategies That Actually Help (and the Ones That Bite)
About Post
There's a moment in every Laravel project when a page gets slow and someone says, "Just cache it."
It works. The page is fast. And a week later, a manager asks why the dashboard still shows yesterday's numbers, or, worse, a user sees a list that belongs to someone else.
Caching is the quickest way to make a slow app fast and the quickest way to make a correct app wrong. The difference is a few habits around keys, invalidation and choosing what to cache in the first place.
First, decide if it deserves a cache
Good cache candidates tick three boxes:
- Expensive: a heavy aggregate query, an external API call, a report that crunches thousands of rows.
- Read far more often than it changes: settings, menus, dashboard stats, lookup lists.
- Allowed to be a little stale: nobody is harmed if the "contracts this month" count is a minute behind.
If a query is slow because it's missing an index, fix the index. A cache on top of a bad query is a painkiller, not a cure, and the query still runs every time the cache expires.
Strategy 1: remember, with keys that include everything
Cache::remember() is the workhorse: return the cached value, or run the closure, store the result and return it.
$summary = Cache::remember(
"tenant:{$tenant->id}:contracts:summary:{$status}:{$locale}",
now()->addMinutes(10),
fn () => ContractSummary::build($tenant, $status, $locale)->toArray()
);
The key is where the dangerous bugs live. Every input that changes the result must be in the key: the user or tenant, filters, page number, locale, role. Cache a per-user summary under a key like contracts:summary and the first user to load it decides what everyone else sees. That's not a performance bug. It's a data leak.
A simple habit: build keys with a consistent pattern, entity:id:what:variant, so they're readable in Redis and easy to find when you need to clear them.
Strategy 2: invalidate on model events
Time-based expiry is the simplest invalidation: the data is at most ten minutes old. When that's not good enough, clear the cache when the data changes.
class Setting extends Model
{
protected static function booted(): void
{
$forget = fn () => DB::afterCommit(fn () => Cache::forget('settings:all'));
static::saved($forget);
static::deleted($forget);
}
}
Two gotchas hide in this small snippet:
- Transactions. If you clear the cache inside a transaction, another request can refill it with the old data before the transaction commits.
DB::afterCommit()waits until the data is really saved (and runs immediately if there's no transaction). - Mass updates skip events.
Setting::where('group', 'mail')->update([...])runs one SQL query and never firessaved. If you use query-builder updates, clear the cache yourself next to them.
Strategy 3: tags or versioned keys for groups
Sometimes one change should clear many keys: every page and filter variation of a tenant's contract list, for example. Cache tags handle that:
Cache::tags(["tenant:{$tenant->id}"])->remember($key, 600, $callback);
// later, when anything for this tenant changes:
Cache::tags(["tenant:{$tenant->id}"])->flush();
Tags only work with stores that support them, like Redis and Memcached. The file, database and dynamodb drivers don't.
If your store doesn't support tags, use a version number in the key instead. Store tenant:42:cache-version, include it in every key for that tenant, and increment it when data changes. Old keys simply stop being read and expire on their own.
Strategy 4: stale-while-revalidate for hot keys
Here's a classic production problem: a popular cache key expires, and fifty requests arrive in the same second. All fifty see an empty cache, and all fifty run the expensive query at once. That's a cache stampede.
Laravel's Cache::flexible() handles this nicely:
$stats = Cache::flexible('dashboard:stats', [300, 900], fn () => Stats::build());
For the first 300 seconds, the value is fresh and served directly. Between 300 and 900 seconds, users still get the stale value instantly, while Laravel recalculates it once in the background after the response is sent. Only after 900 seconds does a request have to wait. For dashboards, it's often the best of both worlds.
For one-off expensive work, Cache::lock() is the other tool: one process takes the lock and builds the value, the others wait or back off.
Strategy 5: warm the cache before anyone asks
If a report is expensive and predictable, don't make the first user of the morning pay for it. Build it on a schedule:
// routes/console.php
Schedule::call(fn () => Cache::put('reports:occupancy', Report::occupancy(), now()->addDay()))
->dailyAt('05:00');
Warming also matters after deploys. If your deploy clears the whole application cache, every hot key goes cold at the same moment, right when traffic hits the new release. Clear only what the deploy actually changed, or warm the important keys straight after.
What I avoid caching
- Anything about money or permissions that must be exact right now. Balances, payment status, "can this user approve this?" Stale here means wrong.
- Whole Eloquent models with relations. They're large when serialised, and when the class changes in a deploy, old cached copies can behave strangely. Cache arrays or IDs instead.
- Cheap queries. A primary-key lookup is already fast. Caching it adds a network round trip to Redis and another thing to invalidate.
- Everything, "just in case". Each cached value is a promise to keep it correct. Make fewer promises.
The rule: before you cache something, answer two questions. What exactly goes in the key? And what event makes it wrong? If you can't answer both, don't cache it yet.
Quick checklist
- Fix the query first; cache what's still expensive.
- Every input that changes the result goes in the key.
- Invalidate after commit, and remember mass updates skip model events.
- Tags on Redis or Memcached; versioned keys elsewhere.
Cache::flexible()or locks for hot keys.- Warm predictable, expensive values on a schedule.
What's the strangest caching bug you've chased? Mine always seem to involve a key that was missing just one variable.

Be first to comment it...