History Encryption with Inertia

January 26th, 2025 • 4 minutes read time

How do you prevent freshly signed-out users from hitting back in their browser, potentially exposing sensitive information? Using Inertia's history encryption feature!

The nature of SPAs means that when we navigate from page to page, we're not actually navigating. It's more like we're manually modifying the browser's history and replacing what's on each page.

Because of this, even after we're signed out, we can navigate back to a previous page and potentially still have access to sensitive information. Of course, it doesn't matter if the authenticated user does this, but if someone else does... we have trouble.

Since Inertia v2, we can now use the History Encryption feature to prevent this.

Here's how it works.

Inertia protects your app's history by "locking" each page’s data using a special code (key). It uses the browser's built-in tools to do this and saves the key in session storage.

When you go back to a page, Inertia "unlocks" (decrypts) the saved data using the same key from session storage. This way, it doesn’t need to ask the server for the page again.

If you tell Inertia to clear the history (which we'll cover in this article), it throws away the old key and creates a new one. Since the old data can't be "unlocked" anymore with the new key, Inertia will fetch fresh data from the server to show the page again. This means that if the user tries to navigate back to a page where they should be authenticated, it won't work, as Inertia is attempting to fetch fresh data!

To use History Encryption with Inertia, your application must be running securely (with HTTPS), even locally.

Ok, let's run through implementing the History Encryption functionality in an Inertia application — from start to finish.

The first step is enabling history encryption within the config/inertia.php config file. If you don't already have the Inertia config file published, run php artisan vendor:publish --provider="Inertia\ServiceProvider".

    // ...

    'history' => [
        'encrypt' => true,
    ],

This globally enables history encryption for all routes/requests. We'll take a look at customising this later.

Next, apply the EncryptHistoryMiddleware to the routes you want to protect. In this case, I've chosen to protect the Dashboard, but you're welcome to add it to any route groups or globally within your application.

use use Inertia\EncryptHistoryMiddleware;

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');
})->middleware(['auth', 'verified', EncryptHistoryMiddleware::class])->name('dashboard');

You'll now need to specify when history is cleared. Most of the time, this will be when a user signs out (but could be at any point in your application).

Let's clear the history in the AuthenticatedSessionController of Laravel Breeze. Adjust this based on how you have your application set up and which starter kit you're using (if any).

class AuthenticatedSessionController extends Controller
{
    public function destroy(Request $request): RedirectResponse
    {
        Auth::guard('web')->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        inertia()->clearHistory();

        return redirect('/');
    }
}

Now, when we sign out, history will be cleared! Immediately pressing back after we sign out will take us right back to the login page (if we were trying to navigate back to a page that required we were authenticated).

And that's it! History Encryption is now enabled in Inertia.

A little earlier, we adjusted the config/inertia.php file to enable history encryption globally.

If you want fine-grained control over when and where this is enabled, you can use the following:

Use this anywhere you want history encryption to be enabled. You'll have to specifically add this code before the response of each page you want to protect.

In my opinion, it's best to simply globally enable history encryption and then disable it where you don't need it.

Passing in false to the encryptHistory method will disable it for that request. As I mentioned, it's a lot easier to globally enable history encryption in your config file and then use encryptHistory(false) to manually disable it where you don't need it.

Whatever strategy you choose, the API is really well thought out, and you have a lot of flexibility!

You probably won't immediately reach to enable history encryption in your Inertia projects, but it's a handy feature when you need to protect sensitive pages and clear history for security reasons a little later down the line.

Remember that Inertia isn't inherently insecure. History encryption adds an additional layer to protect any sensitive information being revealed to users who may be sharing the same browser, for example.

If you found this article helpful, you'll love our practical screencasts.
Author
Alex Garrett-Smith
Share :

Comments

No comments, yet. Be the first!