Config, Route and View Caches
What config:cache, route:cache and view:cache freeze, and why env() returns null once configuration is cached.
php artisan optimize # config:cache + event:cache + route:cache + view:cacheThese caches have nothing to do with the Cache facade. They are build artefacts, written once at deploy time, that let the framework skip work it would otherwise repeat on every request.
What each one freezes
| Command | What it freezes |
|---|---|
config:cache | Every file in config/, merged into one file under bootstrap/cache. The .env file is then no longer loaded. |
route:cache | All route registrations, reduced to a single cached file. |
view:cache | Every Blade template, precompiled instead of compiled on first use. |
event:cache | The auto-discovered event to listener mappings. |
php artisan optimize is the shorthand for all four; the longhand is config:cache, event:cache, route:cache, view:cache. Each has a matching :clear command, and php artisan optimize:clear removes all of them. Note that optimize:clear also flushes the keys in your default cache store, so it is the wrong tool for a stale template.
The env() that returns null
Once configuration is cached, .env is not read at all. env() called anywhere outside config/ returns null: a class that reads env('SHIPPING_KEY') works locally and gets null in production. Read values through config('shipping.key') and let the config file be the only place env() appears.
Route caching serializes your routes, and a closure cannot be serialized. A route written as Route::get('/x', function () { ... }) fails the command; move it into a controller. Route::view() and Route::redirect() are backed by framework controllers, so they cache without complaint.
Example
<?php
// config/shipping.php
// env() is safe here. Config files are the only code read while the
// config cache is being built, so this is where .env values enter.
return [
'base_url' => env('SHIPPING_BASE_URL', 'https://api.example.test'),
'key' => env('SHIPPING_KEY'),
'timeout' => (int) env('SHIPPING_TIMEOUT', 5),
];When to use it
- Cut per-request filesystem work on a deployed application.
- Explain why a new .env value is null in production but fine locally.
- Find the one route closure that makes route:cache fail.
- Write a deploy script that leaves the application cache intact.
More examples
The same value, read two ways
Two lines that behave identically on a developer machine and differently on a deployed server. The failure arrives as an authentication error from the remote API, not as an exception about configuration, which is what makes it hard to place. Grep an unfamiliar application for env( outside config/ before you trust its deployment.
<?php
// app/Services/ShippingClient.php
declare(strict_types=1);
namespace App\Services;
class ShippingClient
{
public function __construct(
// Null in production as soon as config:cache has run.
private ?string $wrong = null,
private ?string $right = null,
) {
$this->wrong = env('SHIPPING_KEY');
$this->right = config('shipping.key');
}
}A closure that blocks route:cache
Route caching is usually introduced late, when the route file is already long, so the error names one route out of hundreds. Keeping route files free of closures from the start means route:cache is never a migration project.
// routes/web.php
// Fails route:cache: the closure cannot be serialized.
Route::get('/health', function () {
return response()->json(['ok' => true]);
});
// Caches cleanly: a controller is a class name and a method name.
Route::get('/health', [HealthController::class, 'show']);
// These are fine too. Both are framework controllers underneath.
Route::view('/about', 'pages.about');
Route::redirect('/old', '/new');Where these commands belong in a deploy
Run optimize after the new code is in place, never before. Prefer it to optimize:clear here: optimize overwrites the four build caches, while optimize:clear would also empty your application cache and leave the first visitors to rebuild every cached value.
php artisan down --render="errors::503"
composer install --no-dev --optimize-autoloader
php artisan migrate --force
# Rewrites the config, event, route and view caches in place.
php artisan optimize
# Terminates long-running services so they pick up the new code.
php artisan reload
php artisan up
Discussion