Laravel App Key Generator

Generate secure application keys for your Laravel projects

Encryption in Laravel: A Practical Guide

Look, I get it. You're building a Laravel app and you need to store sensitive data. API tokens. Payment information. Personal details your users trust you with. And somewhere in the back of your mind, there's this nagging question: "Am I doing this securely?"

That's where Laravel's encryption comes in. And honestly... it's one of those features that just works if you understand what it's doing. Let me walk you through it.

Why Encryption Matters in Your Laravel App

Here's the thing about storing sensitive data in your database: even if someone never gets access to your database, you still need to think about encryption.

Why? Because databases get backed up. They get replicated. Log files capture things. Debug traces expose data. Even with the best security practices, there are a dozen ways sensitive information can leak if it's sitting there in plain text.

Encryption gives you a safety net. If your database backup somehow ends up in the wrong hands, encrypted data is just... gibberish. Useless without the key.

But here's what I've learned the hard way: you can't just encrypt everything and call it a day. You need to understand how Laravel handles encryption, what that APP_KEY actually does, and when you should (and shouldn't) reach for the Crypt facade.

How Laravel's Encryption Actually Works

Laravel uses AES-256-CBC encryption under the hood. If that sounds like alphabet soup, don't worry - here's what you actually need to know.

When you encrypt something in Laravel, it does three important things:

First, it uses your application's encryption key (that APP_KEY in your .env file) along with the AES-256-CBC cipher to scramble your data. This is industry-standard, battle-tested encryption that's used by... well, basically everyone who takes security seriously.

Second - and this is the clever part - it adds a message authentication code (MAC) to the encrypted value. Think of the MAC as a tamper-evident seal. If someone tries to modify the encrypted data, even by a single byte, Laravel will detect it and refuse to decrypt it.

Third, it base64-encodes the whole thing so you can safely store it as a string in your database.

What this means in practice: when you encrypt a value, you're getting a longer string that's completely opaque. And when you decrypt it, Laravel automatically verifies that MAC to make sure nobody's messed with it.

Pretty neat, right?

The Application Key: Your Encryption Foundation

Let's talk about APP_KEY for a second because this is genuinely important.

When you first set up a Laravel project, you run php artisan key:generate. That command creates a random, cryptographically secure key and stuffs it into your .env file:

APP_KEY = base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY=

This key is the foundation of all your encryption. Every time you encrypt something, Laravel uses this key. Every time you decrypt something, it needs this same key.

Here's what you need to remember:

Lose this key, and you lose access to your encrypted data. All of it. There's no "forgot password" flow for encryption keys. If you don't have the key, the data is gone forever.

Change this key, and you can't decrypt old data. At least, not without some extra work (we'll get to that in the key rotation section).

Never commit this key to version control. Your .env file should always be in .gitignore. Always. I mean it. If this key leaks, anyone can decrypt your encrypted data.

So yeah... treat your APP_KEY with respect. Back it up somewhere secure. Maybe even write it down and stick it in a safe if you're old school like that. Just don't lose it.

Using Laravel's Encrypter in Real Code

Okay, enough theory. Let's encrypt some actual data.

The most common way to encrypt values is using the Crypt facade. It's straightforward and does exactly what you'd expect:

use Illuminate\Support\Facades\Crypt;

// Encrypt a string
$encrypted = Crypt::encryptString($apiToken);

// Store it somewhere
$user->api_token = $encrypted;
$user->save();

When you need that value back, you decrypt it:

use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;

try {
    $apiToken = Crypt::decryptString($user->api_token);

    // Use the decrypted token
    $client->setToken($apiToken);
} catch (DecryptException $e) {
    // Something's wrong - the data was tampered with,
    // or encrypted with a different key
    report($e);
    throw new RuntimeException('Unable to decrypt API token');
}

Notice that try-catch block? That's important. Decryption can fail for a few reasons:

Always handle the DecryptException. Don't just let it bubble up and crash your app.

The Encrypted Cast: An Even Easier Way

Here's a Laravel feature that doesn't get enough love: the encrypted cast for Eloquent models.

Instead of manually encrypting and decrypting values every time, you can just tell Laravel "hey, this attribute should be encrypted":

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected function casts(): array
    {
        return [
            'api_token' => 'encrypted',
            'stripe_secret' => 'encrypted',
        ];
    }
}

Now Laravel handles all the encryption and decryption automatically:

// This gets encrypted before saving to the database
$user->api_token = 'secret-token-value';
$user->save();

// This gets decrypted automatically when you access it
$token = $user->api_token; // Returns the plain text value

It just... works. No try-catch needed, no manual calls to Crypt::encryptString(). Laravel handles it all.

You can even encrypt arrays and collections:

protected function casts(): array
{
    return [
        'api_credentials' => 'encrypted:array',
        'metadata' => 'encrypted:collection',
    ];
}

One heads up though: make sure your database column is a TEXT type or larger. Encrypted values are longer than their plain text versions, and you don't want to truncate them accidentally.

A Free Tool to Help You

Speaking of application keys... sometimes you need to generate one outside of the normal php artisan key:generate flow. Maybe you're setting up a new environment and haven't installed Composer yet. Maybe you're provisioning infrastructure with Terraform or CloudFormation and need a key beforehand.

That's why this site offers a free Laravel app key generator. It creates cryptographically secure keys right in your browser - nothing gets stored anywhere.

Your Laravel App Key

Generating...

Quick Usage

Copy the generated key and add it to your .env file:

APP_KEY=

The generator uses the same Web Crypto API that powers secure applications across the web. It's perfect for those times when you need a key quickly and securely without having Laravel installed.

That said, here's when you should use each option:

Use php artisan key:generate when:

Use the browser generator when:

Both methods produce equally secure keys. Use whichever fits your workflow.

Rotating Encryption Keys (Without Breaking Everything)

So here's a scenario that'll happen eventually: you need to rotate your encryption key.

Maybe a developer left and had access to it. Maybe you're being extra cautious after a security incident. Maybe it's just been a few years and you want to rotate it as a best practice.

The problem? If you just change APP_KEY and redeploy, everything breaks. All your encrypted data becomes undecryptable. Your users get logged out. API integrations fail. It's a mess.

In Laravel 11 they introduced a solution: APP_PREVIOUS_KEYS.

Here's how it works. When you want to rotate your key:

Step 1: Generate a new key

php artisan key:generate --show

Step 2: Add your current key to APP_PREVIOUS_KEYS in your .env file

APP_KEY = "base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
APP_PREVIOUS_KEYS = "base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="

Step 3: Update APP_KEY with your new key

APP_KEY = "base64:[your-new-key-here]"
APP_PREVIOUS_KEYS = "base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="

Now here's the magic: Laravel will use the new key for all new encryptions. But when it tries to decrypt something and fails, it automatically tries each previous key until one works.

Your users stay logged in. Old encrypted data still decrypts. New data uses the new key. Gradual, graceful transition.

If you have data that needs to be re-encrypted with the new key (like long-lived API tokens), you'll need to handle that yourself:

use Illuminate\Support\Facades\Crypt;

// Decrypt with old key (automatically uses APP_PREVIOUS_KEYS)
$decrypted = Crypt::decryptString($user->api_token);

// Re-encrypt with new key and save
$user->api_token = Crypt::encryptString($decrypted);
$user->save();

Re-encrypting Model Attributes with Encrypted Casts

Now... if you're using encrypted casts on your models (and honestly, you probably should be), the re-encryption process is a bit different. You can't just grab the raw encrypted value and re-encrypt it with Crypt::encrypt() - that doesn't work because the attribute goes through the cast system.

Instead, you need to trigger the cast's encryption logic. Here's the trick: when you read an encrypted attribute, Laravel decrypts it with the current key. When you write to that attribute, Laravel encrypts it with the current key. So if you just set the attribute to itself, you're essentially saying "decrypt this with the old key, then encrypt it with the new key."

// This triggers re-encryption for encrypted casts
$user->api_token = $user->api_token;
$user->save();

That's it. Read it (decrypts with old key) → Set it (encrypts with new key) → Save it.

Here's a complete Artisan command that handles this properly:

<?php

namespace App\Console\Commands;

use App\Models\User;
use Illuminate\Console\Command;

class ReencryptAttributes extends Command
{
    protected $signature = 'app:reencrypt-attributes';
    protected $description = 'Re-encrypt model attributes after key rotation';

    public function handle(): int
    {
        $this->info('Starting re-encryption process...');

        User::whereNotNull('api_token')
            ->chunk(100, function ($users) {
                foreach ($users as $user) {
                    try {
                        // For encrypted casts: read and write to trigger re-encryption
                        $user->api_token = $user->api_token;
                        $user->save();

                        $this->info("Re-encrypted data for user {$user->id}");
                    } catch (\Exception $e) {
                        $this->error("Failed for user {$user->id}: {$e->getMessage()}");
                    }
                }
            });

        $this->info('Re-encryption complete!');
        return self::SUCCESS;
    }
}

The key difference here: we're not calling Crypt::decrypt() and Crypt::encrypt() directly. We're letting Laravel's cast system handle it by simply reassigning the attribute to itself. This ensures the decryption and encryption happen through the same code path that your application normally uses.

You can run this as a background job to gradually re-encrypt your database without any downtime.

Once everything's been re-encrypted, you can remove the old key from APP_PREVIOUS_KEYS. Just... don't rush it. There's no harm in keeping previous keys around for a while.

Best Practices for Encryption in the Real World

Let me share a few things I've learned from actually using encryption in production Laravel apps.

Don't encrypt data you need to search or filter

This seems obvious once you think about it, but I've seen it trip people up. If you encrypt email addresses, you can't do WHERE email = 'user@example.com' anymore. The database sees encrypted gibberish, not the actual email.

Only encrypt data you'll look up by ID or load all at once. API tokens? Great. User passwords? They should be hashed, not encrypted (totally different thing). Email addresses? Only if you never need to search by them.

Choose TEXT columns for encrypted data

Encrypted values are longer than their plain text versions. A 20-character API token might become 200+ characters when encrypted and encoded. Use TEXT or LONGTEXT in your migrations:

$table->text('encrypted_token')->nullable();

Don't try to get cute with VARCHAR(255). You'll truncate the encrypted value and then you can't decrypt it. Ask me how I know.

Always handle DecryptException

I mentioned this earlier, but it's worth repeating. Decryption can fail. Maybe the key changed. Maybe data got corrupted. Maybe someone manually edited the database (it happens).

Always wrap decryption in try-catch:

try {
    $value = Crypt::decryptString($encrypted);
} catch (DecryptException $e) {
    // Log it, alert someone, fall back gracefully
    report($e);
    return null;
}

Back up your APP_KEY securely

Store it in your password manager. Document it in your infrastructure-as-code. Put it in AWS Secrets Manager or HashiCorp Vault. Just have a backup somewhere that's not the same server running your application.

Seriously. Future you will thank me.

Remember: encryption isn't hashing

Quick reminder because this confuses people: encryption is reversible. You can get the original value back. Hashing is one-way - you can't reverse it.

Passwords should be hashed (using Hash::make()), not encrypted. API tokens that users need to see should be encrypted. Know the difference and use the right tool.

Consider what happens if keys leak

Even with encryption, think about your threat model. If someone gets both your database and your APP_KEY, they can decrypt everything. Encryption protects against database-only leaks.

For super-sensitive data, you might need additional layers: field-level encryption with separate keys, envelope encryption, or external key management services.

But for most apps? Laravel's built-in encryption is perfectly fine.

Wrapping Up

Encryption in Laravel is one of those features that seems intimidating at first, but once you understand it, it's actually pretty straightforward.

You've got strong encryption (AES-256-CBC) with tamper detection (MAC). You can use the Crypt facade directly or let Eloquent handle it with casts. You can rotate keys gracefully without breaking everything. And Laravel handles all the hard cryptographic stuff so you don't have to.

Just remember the basics:

And honestly? That's most of what you need to know. Start simple, use the built-in tools, and only get fancy when you actually need to.

Your users trust you with their data. Laravel's encryption helps you honor that trust. Use it well.