Laravel can serve fast pages, but the framework is only one part of the request. Database queries, external services, PHP workers, caches, templates, images, JavaScript, and third-party scripts all contribute to the experience measured in the browser.
This guide uses current Laravel 13 conventions. For an older application, check that version's upgrade guide before copying configuration or commands.
Profile the full request
Start with a representative production route and separate server time from browser time.
On the server, record:
- route and middleware duration;
- query count and cumulative query time;
- cache hits and misses;
- external API and storage calls;
- queue work accidentally performed inline;
- response size, status, and cache headers;
- PHP worker saturation and database connection pressure.
In the browser, inspect the network waterfall and the LCP, CLS, TBT, FCP, and Speed Index metrics. A slow LCP can begin with server response time, but it can also come from late image discovery, client rendering, fonts, or main-thread work. How to Check Website Speed provides a repeatable testing workflow.
Profile with production-like data and configuration. Debug tooling is useful locally but can add overhead and expose sensitive information, so do not enable it publicly to measure production.
Build deployment caches safely
Laravel's current deployment documentation recommends generating optimized caches during deployment:
php artisan optimize
This caches configuration, events, routes, and views. Run it after the release artifact and environment are in place, and before traffic reaches the new version. If configuration is cached, calls to env() outside configuration files do not behave as they do in an uncached local environment. Read environment values in config files and consume them through config() in application code.
Use the granular commands when your release process needs explicit stages:
php artisan config:cache
php artisan event:cache
php artisan route:cache
php artisan view:cache
Treat php artisan optimize:clear as an operational action, not a routine request-path fix. A deployment should create a complete, coherent cache set for the exact code and environment being released.
Cache application work intentionally
Laravel's cache system supports multiple stores. Current configuration uses CACHE_STORE, not the older CACHE_DRIVER name used in many legacy examples.
CACHE_STORE=redis
Redis is a common shared store, but the correct choice depends on availability, persistence, latency, and operating model. A file cache may be local to one container and therefore unsuitable when several application instances need the same entries.
Cache stable, expensive work with a clear key and lifetime:
use Illuminate\Support\Facades\Cache;
$products = Cache::remember(
"catalog:featured:v3",
now()->addMinutes(10),
fn () => Product::query()
->published()
->with("category")
->latest("published_at")
->limit(24)
->get()
);
Version or invalidate the key when publication changes. Do not cache account-specific or authorization-dependent output under a shared key. Protect the application against a cache outage and avoid a synchronized cache stampede on expensive recomputation.
HTTP caching is separate. Public responses and versioned assets can benefit from CDN or reverse-proxy caching, but cookies, localization, authentication, and personalized content require correct cache-control and variation rules.
Reduce database cost
Inspect queries generated by Eloquent instead of assuming the ORM is slow. Common causes are N+1 relationships, missing indexes, unbounded result sets, and repeated aggregation.
Use eager loading for relationships rendered in a list:
$articles = Article::query()
->published()
->with(["author:id,name", "categories:id,name,slug"])
->select(["id", "author_id", "slug", "title", "summary"])
->paginate(24);
Select only useful columns when the model contains large fields, but include relationship keys needed by Eloquent. Add indexes based on real WHERE, join, and ordering patterns, then inspect the database query plan. An index that helps one lookup can add write and storage cost elsewhere.
For high-frequency aggregates, consider a maintained summary, a bounded cache, or asynchronous computation. Use cursor or chunk processing for large background data jobs rather than loading an entire table into memory.
Database connection pooling is deployment-specific. Laravel's current database documentation notes that transaction-mode poolers such as PgBouncer can suit application queries while migrations and some maintenance tasks require a direct connection. Keep migration and runtime connection roles explicit.
Move slow work out of the request
Email, image processing, webhooks, indexing, large exports, and other durable side effects generally belong on a queue. Return a truthful response only after the transaction that must succeed has committed, then let an idempotent job handle the asynchronous work.
Run queue workers under a process supervisor, set timeouts and retry policies based on the job, and restart workers during deployment so they load the new application code. Monitor failed jobs and queue latency; moving work to a queue does not remove its operating cost.
Ship production assets with Vite
Laravel 13 uses Vite as its standard asset pipeline. The official Vite integration guide documents production builds and versioned assets:
npm ci
npm run build
@vite(['resources/css/app.css', 'resources/js/app.js'])
Do not combine a Vite setup with examples from Laravel's older asset pipeline. Build assets once for the release and deploy the generated manifest and files together. If assets live on a separate CDN host, configure the supported asset URL and verify that HTML points to the versioned production files.
Use route-level imports for large interactive features, remove unused dependencies, and measure third-party scripts. A server-rendered Blade page can still have poor TBT when it ships too much JavaScript.
Create an image delivery pipeline
Do not resize every large upload during the page request. Validate and transform images at ingestion or in a queue, then store the variants needed by the design.
If you use Intervention Image, target a supported major version. Version 3 uses an ImageManager, read(), resizing methods such as scale(), and encoders such as WebpEncoder; older make() and encode('webp', ...) snippets belong to a different API. Its version 3 output documentation is the source for current syntax.
Whichever library you choose, generate responsive dimensions, retain metadata about width and height, and serve them with srcset and sizes. Do not lazy-load the likely LCP image. Use meaningful alternative text for informative images and an empty alt for decoration.
Configure PHP and workers for the release model
Enable OPcache in production, then align validation with deployment behavior. The PHP OPcache manual is precise:
- with
opcache.validate_timestamps=1,opcache.revalidate_freq=0checks for updates on every request; - with timestamp validation disabled, changed files are not picked up until OPcache is reset, invalidated, or the web server is restarted.
An immutable release plus an explicit PHP-FPM restart can safely use a different policy from a server where files change in place. Do not disable validation without a dependable activation step, or workers may continue executing old code.
Choose PHP-FPM worker counts from measured memory, CPU, request time, and downstream capacity. More workers can increase concurrency while exhausting RAM or database connections. Load test through the real reverse proxy and monitor latency percentiles, errors, queueing, and resource saturation.
Verify the browser result
After each change:
- deploy to a production-like environment;
- run several equivalent Lighthouse tests and compare a representative median;
- inspect the trace or waterfall to verify the intended cause changed;
- exercise authenticated and interactive journeys;
- monitor field Core Web Vitals after production traffic reaches the release.
A Lighthouse score is a lab diagnostic, not a ranking guarantee. Does Page Speed Affect SEO? explains the distinction. You can review dated measurements on IndieTools Speed and use the LCP guide or TBT guide for the metric that remains slow.


