Aller au contenu principal
Making DigitalMaking Digital
Note technique

Après un déploiement, env() renvoie null ou une route répond 404 : les caches de Laravel

En bref

Après php artisan config:cache, Laravel ne charge plus le fichier .env : env() appelé en dehors des fichiers de config/ renvoie null pour ses variables. Il faut appeler env() uniquement dans config/, lire les valeurs avec config() partout ailleurs, et garder le cache en production. Une route ajoutée après route:cache répond 404 jusqu'à route:clear ou un nouveau route:cache ; cache:clear ne touche à aucun de ces deux caches.

Le code fonctionne en local, le déploiement s'est bien passé, et pourtant en production une clé d'API arrive vide parce que env('MA_CLE') renvoie null, ou une page que vous venez d'ajouter répond 404 alors que sa route est bien dans routes/web.php. Dans les deux cas, la cause est la même : les caches que Laravel construit au déploiement, celui de la configuration et celui des routes. La bonne réponse n'est pas de les désactiver, mais d'écrire le code pour qu'il fonctionne avec eux, et de les reconstruire au bon moment.

Pourquoi env() renvoie null après config:cache ?

php artisan config:cache regroupe tous les fichiers de config/ dans un seul fichier, bootstrap/cache/config.php. Une fois ce cache en place, la documentation de Laravel est claire : le fichier .env n'est plus chargé, ni pendant les requêtes ni pendant les commandes Artisan. env() ne renvoie alors que les variables d'environnement définies au niveau du système, et null pour celles qui ne sont que dans .env.

Nous l'avons reproduit sur une application neuve, avec PILOT_VAR=bonjour dans .env, ce fichier de configuration et une route de test :

PHP
// config/pilot.php
return [
    'var' => env('PILOT_VAR'),
];
PHP
// routes/web.php
Route::get('/pilot-env', function () {
    return response()->json([
        'env' => env('PILOT_VAR'),
        'config' => config('pilot.var'),
        'external' => env('EXT_VAR'),
    ]);
});

EXT_VAR est une variable définie au lancement du serveur PHP, pas dans .env. Résultat :

Appel dans la route Sans cache Après config:cache
env('PILOT_VAR') "bonjour" null
config('pilot.var') "bonjour" "bonjour"
env('EXT_VAR') "systeme" "systeme"

La valeur n'a pas disparu : elle a été lue au moment du config:cache et figée dans la configuration. C'est env() appelé en dehors de config/ qui ne la trouve plus.

Comment corriger : env() dans config/, config() partout ailleurs

La règle de la documentation tient en une ligne : n'appeler env() que dans les fichiers de config/, et lire les valeurs avec config() partout ailleurs (contrôleurs, services, jobs, vues). Pour une clé d'API :

PHP
// config/services.php
'meteo' => [
    'key' => env('METEO_API_KEY'),
],
PHP
// Partout ailleurs dans l'application
$key = config('services.meteo.key');

Après config:cache, env('METEO_API_KEY') renvoie null et config('services.meteo.key') renvoie bien la clé.

L'ancien réflexe, lancer php artisan config:clear pour "réparer", supprime le fichier de cache : env() refonctionne, mais l'application perd le cache et le problème revient au déploiement suivant. La documentation recommande au contraire de lancer config:cache à chaque déploiement en production, et de ne pas le faire en développement local.

Modifier le .env en production

Le cache ne relit pas .env. Dans notre test, passer PILOT_VAR à au-revoir n'a rien changé tant que php artisan config:cache n'avait pas été relancé. Après chaque modification du .env sur le serveur, il faut reconstruire le cache, y compris pour un réglage comme la durée de conservation des logs.

Pourquoi une nouvelle route répond 404 après un déploiement ?

php artisan route:cache enregistre toutes les routes dans bootstrap/cache/routes-v7.php, et c'est ce fichier qui est chargé à chaque requête. Une route ajoutée après sa création reste inconnue tant qu'il n'est pas reconstruit ou supprimé : c'est le cas d'un déploiement qui copie le nouveau code sans relancer route:cache.

Dans notre test, une route /nouvelle-page ajoutée après route:cache répond 404, et php artisan route:list --path=nouvelle ne la trouve pas. Après php artisan route:clear, elle répond 200. Après un nouveau php artisan route:cache, elle répond toujours 200, puisqu'elle fait désormais partie du cache.

cache:clear vide-t-il le cache des routes ?

Non. php artisan cache:clear vide le cache applicatif, celui que vous remplissez avec Cache::put() ou Cache::remember(). Il ne touche ni à bootstrap/cache/config.php ni à bootstrap/cache/routes-v7.php : après un cache:clear, notre route restait en 404 et env() renvoyait toujours null.

Commande Effet
config:cache / config:clear crée / supprime le cache de la configuration
route:cache / route:clear crée / supprime le cache des routes
cache:clear vide le cache applicatif, rien d'autre
optimize met en cache la configuration, les événements, les routes et les vues
optimize:clear supprime ces caches, les fichiers compilés et vide aussi le cache applicatif

Pour savoir où en est un serveur, php artisan about --only=cache affiche CACHED ou NOT CACHED pour la configuration, les événements, les routes et les vues.

Dans quel ordre lancer les commandes au déploiement ?

php artisan optimize enchaîne config:cache, event:cache, route:cache et view:cache. Il doit passer une fois le nouveau code et le .env définitif en place, sinon il fige l'état précédent. Voici la séquence que nous avons exécutée de bout en bout :

Terminal
# Une fois le nouveau code récupéré (git pull ou équivalent)
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize
php artisan reload

php artisan reload relance les services qui tournent en continu (dans notre test, les workers de file d'attente et le planificateur) pour qu'ils utilisent le nouveau code. Si l'application affiche des fichiers envoyés par ses utilisateurs, vérifiez aussi le lien public/storage. En cas de doute sur un serveur, php artisan optimize:clear repart de zéro et php artisan optimize reconstruit tout ; gardez en tête que optimize:clear vide aussi le cache applicatif.

Ce sont des réglages que nous mettons en place sur les applications que nous hébergeons et maintenons, comme sur les applications sur mesure que nous développons. Un déploiement qui casse en production ? Parlons-en.

À retenir
  • env() seulement dans config/, config() partout ailleurs.
  • Garder config:cache (ou optimize) en production, et le relancer après chaque modification du .env.
  • Une route en 404 après un déploiement : route:clear ou un nouveau route:cache, jamais cache:clear.
  • Au déploiement : code, dépendances, migrations, puis php artisan optimize et php artisan reload.

Testé avec Laravel 13.37.0 et PHP 8.3.6.

Questions fréquentes

Faut-il lancer config:cache en développement local ?

Non. La documentation de Laravel le réserve au déploiement en production : en local, les options de configuration changent souvent et le cache obligerait à le reconstruire à chaque modification.

env() lit-il encore les variables définies par le serveur après config:cache ?

Oui. Une fois la configuration en cache, env() ne renvoie plus que les variables d'environnement du système. Dans notre test, une variable passée au lancement du serveur PHP restait lisible, alors que celle du .env renvoyait null.

Comment vérifier qu'une route fait partie du cache ?

php artisan route:list --path=... lit le cache quand il existe. Si la route n'y apparaît pas alors qu'elle est dans routes/web.php, le cache est périmé : relancez php artisan route:cache.

Les routes définies avec une closure peuvent-elles être mises en cache ?

Oui. Dans notre test sous Laravel 13, des routes déclarées avec une closure ont été mises en cache par route:cache et servies normalement.

Making Digital

Nous connectons les logiciels des PME, automatisons leurs tâches répétitives et créons des assistants IA, depuis 2010. Nos articles partent de nos projets réels.

Notre parcours
Une question sur ce sujet ?

Parlons-en 30 minutes.

Vous saurez si votre problème se règle, comment, et en combien de temps. Sans engagement.

Un échange de 30 minutes, en visio ou par téléphone

Gratuit, sans engagement. On parle de votre problème, pas de technique.