Learn how to implement feature flags in Laravel using custom middleware. Gate new functionality safely, manage configuration, and decouple deployment from release.
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.
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.
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:
Bashphp artisan make:middleware EnsureFeatureEnabled
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.
PHPnamespace 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); } }
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:
PHPRoute::middleware(['auth:sanctum', 'feature:beta_dashboard'])->group(function () { Route::get('/beta/stats', [DashboardController::class, 'betaStats']); });
new_reporting_tool to your config/features.php.feature:new_reporting_tool middleware to a new API endpoint..env file to FEATURE_NEW_REPORTING_TOOL=true and verify the endpoint responds; then set it to false and confirm it returns a 404.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.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.
Learn to build production-ready integrations by validating webhook signatures and offloading processing to queues to ensure security and system reliability.
Read moreMaster non-breaking migrations and safe rollback procedures. Learn the expand-and-contract pattern to evolve your database schema without production downtime.
Using Middleware for Feature Flags