Back to Blog
Lesson 54 of the Intermediate Laravel: Real-World Application Patterns course
LaravelJune 27, 20263 min read

Using Middleware for Feature Flags in Laravel

Learn how to implement feature flags in Laravel using custom middleware. Gate new functionality safely, manage configuration, and decouple deployment from release.

LaravelMiddlewareFeature FlagsDeploymentArchitecturephpbackend

Previously in this course, we explored implementing middleware for API security. We focused on request authorization, but today we’re shifting our focus to controlling application behavior itself. In this lesson, we’ll implement a feature toggle system that allows you to gate specific routes or features behind configuration settings, enabling you to decouple your deployment from your code's availability.

Why Feature Flags Matter

In a production environment, you rarely want to deploy code that is immediately accessible to everyone. Whether you are running an A/B test or rolling out a complex dashboard migration, you need a way to turn features on or off without touching your deployment pipeline.

Feature flags (or toggles) allow you to ship unfinished or experimental code to production, keeping it dormant until you are ready to flip the switch. By using middleware, we can enforce these gates at the HTTP layer, ensuring unauthorized users never even trigger the logic for a hidden feature.

Implementing a Feature Toggle System

We will create a EnsureFeatureEnabled middleware. This middleware will check our config/features.php file to see if a specific feature key is set to true.

First, create the configuration file:

PHP
#6A9955">// config/features.php
return [
    'beta_dashboard' => env('FEATURE_BETA_DASHBOARD', false),
    'api_v2_access' => env('FEATURE_API_V2', false),
];

Now, generate the middleware:

Bash
php artisan make:middleware EnsureFeatureEnabled

The Middleware Logic

Your middleware needs to accept the feature name as a parameter from the route definition. If the feature is disabled, we will return a 404 Not Found or a custom response.

PHP
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureFeatureEnabled
{
    public function handle(Request $request, Closure $next, string $feature): Response
    {
        if (!config("features.{$feature}", false)) {
            abort(404, 'This feature is currently unavailable.');
        }

        return $next($request);
    }
}

Registering and Using the Middleware

Register your middleware in bootstrap/app.php (or app/Http/Kernel.php if using older Laravel versions):

PHP
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'feature' => \App\Http\Middleware\EnsureFeatureEnabled::class,
    ]);
})

Now, apply it to your routes in routes/api.php:

PHP
Route::middleware(['auth:sanctum', 'feature:beta_dashboard'])->group(function () {
    Route::get('/beta/stats', [DashboardController::class, 'betaStats']);
});

Hands-on Exercise

  1. Define a new flag: Add new_reporting_tool to your config/features.php.
  2. Protect a route: Apply the feature:new_reporting_tool middleware to a new API endpoint.
  3. Toggle it: Change your .env file to FEATURE_NEW_REPORTING_TOOL=true and verify the endpoint responds; then set it to false and confirm it returns a 404.

Common Pitfalls

  • Configuration Caching: If you run php artisan config:cache in production, you must clear the cache (php artisan config:clear) after changing your .env flags, or your changes won't take effect.
  • Naming Collisions: Keep your feature flags namespaced in your config file to avoid collisions with other packages.
  • Stale Flags: Feature flags are technical debt. Once a feature is permanently released, remove the flag and the associated middleware calls. I recommend creating a ticket in your project board to "Remove Flag X" immediately after a successful rollout.

Recap

We’ve successfully decoupled our deployment from our feature releases. By using middleware, we’ve created a clean, readable way to gate code paths. This approach is far superior to cluttering your controllers with if (config(...)) checks, as it keeps your business logic controllers focused on their primary domain—a theme we've emphasized since we started architecting for maintainability.

As you scale this further, you might consider externalizing these flags to a service if you need to toggle features in real-time without modifying environment variables, similar to how we handle deploying Laravel applications with zero downtime.

Up next: We will dive into Job Chaining and Batching, moving from simple background tasks to complex, multi-step workflows.

Similar Posts