1. Environment Configuration & Dependency Management
A production Laravel app should never rely on the default .env values that ship with the framework. Start by:
- Never commit
.env: Add it to.gitignoreand store secrets in your CI/CD vault (e.g., GitHub Secrets, HashiCorp Vault, AWS Parameter Store). - Use environment‑specific config files: Create
config/app.phpentries that read fromenv()with sensible fallbacks, but avoid hard‑coding defaults that differ between local and production. - Lock dependencies: Run
composer install --no-dev --optimize-autoloaderon the build server. Commit the generatedcomposer.lockto guarantee identical packages across deployments. - PHP version alignment: Declare the required PHP version in
composer.json("php": ">=8.2"). CI should fail if the build server runs an older interpreter.
# Example CI step
composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache
Caching the config, routes, and compiled views removes the overhead of parsing files on every request and is a prerequisite for high‑throughput workloads.
2. Database Strategy: Migrations, Seeds, and Read Replicas
Migrations
Treat migrations as code. Run them only in a controlled deployment window and never on the fly in production. Use php artisan migrate --force inside your CI pipeline after a successful build.
# Deploy step
php artisan migrate --force
Seeds & Data Integrity
Separate seed data that is required for the application to start (e.g., admin role, feature flags) from large reference tables. Keep these seeds idempotent so re‑running them does not create duplicates.
Read Replicas & Connection Pools
For read‑heavy workloads, configure a replica connection in config/database.php and route read queries using Laravel's read/write splitting:
'connections' => [
'mysql' => [
'read' => [
'host' => env('DB_READ_HOST', 'replica.myapp.com'),
],
'write' => [
'host' => env('DB_WRITE_HOST', 'primary.myapp.com'),
],
'driver' => 'mysql',
// other options …
],
],
When you enable read/write splitting, make sure any transaction that writes also reads from the primary to avoid stale data.
3. Caching, Queues, and Asynchronous Work
Cache Layer
Laravel supports multiple cache drivers. In production, Redis is the default choice because it offers both key/value caching and pub/sub for queue back‑ends.
// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),
Cache expensive queries with remember():
$users = Cache::remember('active_users', now()->addMinutes(10), function () {
return User::where('active', true)->get();
});
Queues
Never run time‑consuming jobs in the request cycle. Offload them to a queue driver such as Redis or Amazon SQS. Define a dedicated queue:work supervisor process (e.g., using Supervisor on Linux) that restarts automatically on failure.
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3
numprocs=4
autostart=true
autorestart=true
user=www-data
stopwaitsecs=3600
Rate Limiting & Throttling
Laravel's built‑in rate limiter can protect APIs from abuse without adding external middleware.
Route::middleware('throttle:60,1')->group(function () {
Route::get('/orders', [OrderController::class, 'index']);
});
4. Security Hardening
HTTPS Everywhere
Terminate TLS at the load balancer (AWS ALB, Cloudflare, etc.) and enforce APP_URL with https. In AppServiceProvider force HTTPS for all generated URLs:
public function boot()
{
if (app()->environment('production')) {
URL::forceScheme('https');
}
}
HTTP Security Headers
Add a middleware that injects Content‑Security‑Policy, X‑Content‑Type‑Options, Referrer-Policy, and X‑Frame‑Options headers. Packages like spatie/laravel-csp simplify this.
// app/Http/Kernel.php
protected $middleware = [
// …
\Spatie\Csp\Middleware\AddCspHeaders::class,
];
Input Validation & Mass Assignment
Never rely on $request->all() for model creation. Use Form Request classes that define explicit validation rules and the $fillable array on Eloquent models.
class StorePostRequest extends FormRequest
{
public function rules()
{
return [
'title' => 'required|string|max:255',
'body' => 'required|string',
];
}
}
// Controller
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
Session & Cookie Settings
Set SESSION_SECURE_COOKIE=true, SESSION_HTTP_ONLY=true, and SESSION_SAME_SITE=lax in .env. For API‑only apps, consider stateless JWT tokens instead of server‑side sessions.
Regular Audits
Run php artisan security:check (provided by laravel/security-checker) in CI to detect known Composer vulnerabilities.
5. Monitoring, Logging, and Zero‑Downtime Deployments
Centralized Logging
Pipe Laravel logs to a structured logger like Monolog with a JSON formatter and ship them to a log aggregation service (Papertrail, Loggly, Elastic Stack).
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'stderr'],
],
'stderr' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'formatter' => Monolog\Formatter\JsonFormatter::class,
'with' => ['stream' => 'php://stderr'],
],
],
Application Performance Monitoring (APM)
Integrate an APM agent (e.g., Laravel Telescope for internal debugging, or third‑party services like New Relic, Datadog). Telescope should be enabled only in staging, never in production, to avoid overhead.
if (app()->environment('local', 'staging')) {
$app->register(TelescopeServiceProvider::class);
}
Health Checks
Expose a /health endpoint that returns HTTP 200 only when critical services (DB, cache, queue) are reachable. Use Laravel's route closure:
Route::get('/health', function () {
try {
DB::connection()->getPdo();
Cache::store()->ping();
return response()->json(['status' => 'ok']);
} catch (Throwable $e) {
return response()->json(['status' => 'error'], 503);
}
});
Load balancers can poll this endpoint for automatic instance replacement.
Zero‑Downtime Deploys
Leverage Blue‑Green or Canary deployments with tools like Laravel Envoy, GitHub Actions, or Terraform. The typical flow:
- Build artifact (Docker image or compiled assets).
- Deploy to a new target group.
- Run database migrations against the new version.
- Shift a small percentage of traffic.
- Monitor errors; if none, promote to 100 %.
If an issue appears, roll back by routing traffic back to the previous version and reverting the code base.
Takeaway
Production‑grade Laravel isn’t about a single setting; it’s a disciplined checklist that covers configuration, data handling, performance, security, and observability. By applying the practices above you can reduce emergency tickets, improve response times, and give your engineering team the confidence to iterate faster. Start by automating the checklist in your CI pipeline and treat each item as a gate that must pass before code reaches users.
- Laravel
- Security
- Php
- Devops
- Performance