Skip to content

Laravel configuration, exception handling, and logging

Status: Complete. Last reviewed 2026-08-27.

Environment variables are deployment input; Laravel configuration is the typed application-facing layer built from that input. Read env() only in config/*.php, give values meaningful configuration names, and let application code use config(). This makes one resolved configuration graph visible to tests, workers, HTTP requests, and diagnostics.

config:cache evaluates the configuration files and writes one optimized artifact. Once cached, Laravel does not load .env during normal requests or Artisan commands, so an env() call outside configuration can unexpectedly return only a system-level value or its default. Runtime changes to .env do not update a running process or the cached artifact. Build configuration with the release’s intended environment, validate required values, then restart long-lived processes.

Secrets should come from an access-controlled deployment secret mechanism and must not enter source control, cache artifacts distributed too broadly, logs, exception messages, or diagnostic output. Laravel’s encrypted environment-file commands protect a committed encrypted file only if the decryption key is managed separately. APP_KEY is cryptographic identity, not an ordinary password to rotate casually: losing or replacing it can invalidate encrypted cookies and make stored ciphertext unreadable unless a previous-key migration is planned.

Configuration cache, route cache, event cache, and view cache solve different problems. Route cache serializes route definitions and requires cacheable definitions; view cache precompiles Blade templates; neither caches HTTP responses. optimize builds several framework caches and optimize:clear clears them, but production release ordering still matters. Never mutate a shared live release directory while requests use half-old, half-new artifacts.

Reporting and rendering are separate boundaries

Section titled “Reporting and rendering are separate boundaries”

An exception has at least two audiences. Reporting sends diagnostic evidence to logs or an external tracker. Rendering turns the failure into an HTTP or console response safe for the caller. Laravel 13 configures these behaviors in bootstrap/app.php through withExceptions(), with type-specific reporting/rendering callbacks, log levels, ignored exception types, context, duplicate suppression, and throttling.

Report unexpected failures with a stable error/correlation ID, exception class, operation, actor and tenant identifiers where lawful, release, dependency, and retry metadata. Do not log passwords, tokens, cookies, authorization headers, raw payment data, or entire request bodies. Expected domain rejection, validation failure, authentication failure, authorization denial, missing resource, rate limit, dependency outage, and programmer defect should not all become an identical 500 or identical alert.

Rendering is a protocol decision. HTML clients may receive an error page; JSON APIs should receive a stable documented envelope and appropriate status without a trace, SQL, filesystem path, configuration, or secret. Laravel determines whether a request expects JSON from request negotiation and can be customized. A domain exception can map to 409 or 422 if that contract is deliberate. Unknown exceptions remain generic 500 responses. APP_DEBUG must be false in production because detailed error pages can disclose environment and application state.

Do not catch Throwable at every controller merely to log and rethrow: that produces duplicate noise and can destroy framework response mapping. Catch at a boundary only to recover, translate, compensate, add otherwise unavailable context, or classify retryability. Preserve the original exception as the previous cause. A queued job and a web request need different rendering, but can share exception taxonomy and reporting context.

Laravel logging is built on Monolog channels. A channel selects a handler/configuration; a stack fans a record to several channels. Channels are destinations and transports, not severity categories. Use structured context fields rather than encoding every value into prose. Stable field names enable queries and alerts: request_id, trace_id, tenant_id, user_id, job_id, attempt, release, and dependency operation.

Laravel Context can carry scoped metadata across logs and, for supported queued work, hidden context across the process boundary. Treat propagated values as bounded metadata, not a place for request objects or secrets. In long-lived workers, clear or replace per-operation context so one tenant’s fields do not leak into another job.

Choose severity by required human or automated response. Debug is development evidence; info records meaningful normal events; warning signals degraded or recoverable behavior; error means an operation failed; critical and above should be rare conditions demanding urgent attention. Logging every handled 404 as an error creates alert fatigue. Conversely, sampling must never remove all evidence of low-volume security or correctness failures. Apply rate limits, deduplication, or exception throttling with counters so suppressed volume remains measurable.

Logging can fail or block. A synchronous network handler on the request path adds dependency latency. A full local disk can break file logging or the application itself. stack channels can be configured to ignore member exceptions, which preserves availability but risks silent telemetry loss. Decide that trade-off and monitor the pipeline independently. Log retention, access, regional storage, and deletion are security and compliance controls.

  1. A secret or endpoint changes in the platform environment.
  2. The deployed release still has a configuration cache built with the old value, and workers remain alive.
  3. New FPM processes may load one value while workers retain another; direct env() calls create further disagreement.
  4. Requests fail only on one execution path, producing misleading intermittent symptoms.
  5. Recovery requires rebuilding configuration in the right environment and gracefully restarting every long-lived runtime—not editing .env alone.

Prevent this with immutable releases, startup validation, redacted configuration fingerprints, release IDs in telemetry, and a deployment step that reloads workers after cache generation.

Current: fresh Laravel 13 applications configure exception handling in bootstrap/app.php; typed Config retrieval, exception duplicate suppression/throttling, and Context are available. Common: Laravel 11–12 share the streamlined structure. Legacy: older applications commonly use app/Exceptions/Handler.php and kernel files. Explain the responsibility rather than insisting on one file location.