How Laravel builds the key
key:generate asks Encrypter::generateKey()
for random_bytes() of the length your cipher needs, base64-encodes them and adds the
base64: prefix. When the app boots, the encryption service provider strips that prefix and
decodes the rest, so what the encrypter actually receives is raw bytes. This page does the same with your browser's
cryptographically secure random generator: 32 bytes for AES-256 (44 characters of base64), 16 bytes for AES-128 (24 characters).
Laravel supports four ciphers: AES-128-CBC, AES-256-CBC,
AES-128-GCM and AES-256-GCM. A fresh application ships with
'cipher' => 'AES-256-CBC' in config/app.php. The key length has to
match that setting exactly; a 16-byte key with an AES-256 cipher throws
RuntimeException: Unsupported cipher or incorrect key length, and an empty
APP_KEY throws MissingAppKeyException
("No application encryption key has been specified").
# .env APP_KEY=base64: # Or, with artisan available, print one without touching .env: php artisan key:generate --show
What APP_KEY protects, and what it does not
- Cookies. The
EncryptCookiesmiddleware encrypts every cookie that is not on its exception list, including the session cookie and the "remember me" cookie. - Anything you encrypt yourself with
Crypt::encryptString()orencrypt(), and Eloquent attributes cast asencrypted,encrypted:arrayand friends. - Queued jobs that implement
ShouldBeEncrypted, and the session payload itself whenSESSION_ENCRYPT=true. - Signed URLs.
URL::signedRoute()andtemporarySignedRoute()sign with an HMAC keyed byapp.key. - Not password hashes.
Hash::make()uses bcrypt or Argon2 with a per-hash salt and never readsAPP_KEY, so changing the key does not lock anyone out of their password.
What happens when you change it
With a new key and nothing else, data encrypted under the old key can no longer be read. Existing session cookies fail to
decrypt, so every user is logged out. Encrypted model attributes throw DecryptException: The MAC is invalid
when you access them. Signed links you already emailed stop validating, and encrypted jobs waiting in the queue fail.
To rotate without that breakage, move the old key to APP_PREVIOUS_KEYS, a comma-separated
list that config/app.php reads into app.previous_keys.
Laravel always encrypts and signs with the current key, but when decrypting it tries the current key first and then each
previous one, and signed URLs and cookies are accepted if any of the keys matches.
APP_KEY=base64:<new key from this page> APP_PREVIOUS_KEYS=base64:<the key you are replacing>
Values only get re-encrypted with the new key when they are written again, so if you want to drop the old key eventually, re-save your encrypted columns first.
Keep it out of git
Anyone holding your key can decrypt your cookies and encrypted columns and forge signed URLs that your app will accept.
That is why .env is in Laravel's default .gitignore and
.env.example ships with an empty APP_KEY=. Give each
environment its own key, store production's in your host's secret manager or environment settings, and if a key was ever
committed, treat it as leaked and rotate it as described above.
Setting up a new Laravel app?
Once the key is in place, the next things to configure are usually locale and translations. Our getting started guide and the five-minute setup walkthrough cover adding live translations, the locale code lookup tells you which folder name to give each language, and the other free tools test plurals, find hardcoded strings and convert lang files.