jmikola/insecure-routes-bundle
Symfony bundle for enforcing HTTPS on sensitive routes: mark routes as secure-only and the listener denies or redirects any request arriving over plain HTTP. Simple YAML configuration, works with your existing firewall and route names.
Reality check before anything else: jmikola/insecure-routes-bundle is a Symfony 2.x bundle. It registers through the Symfony kernel/bundle model and hooks the Symfony routing layer at container-build time. You cannot composer require it into a Laravel app — it drags in Symfony2-era routing and DI components that conflict with illuminate/routing and illuminate/container. There is no adapter, no shim, and no reason to expect one. So the day-to-day value here is the pattern, not the package. Everything below is framed that way.
What the package actually does: Symfony routes can declare a scheme requirement (schemes: [https] or requirements: { _scheme: https }). That's correct for production and painful locally, because a route with an HTTPS requirement refuses to serve over plain HTTP. The bundle walks the compiled route collection and strips the scheme requirement, so http://app.local/... resolves. It is designed to be registered in dev and test kernels only.
Where to look first in the repo: the DependencyInjection/ extension and any compiler pass — that's where the route collection is mutated — plus the README for the jmikola_insecure_routes config key and the registerBundles() gating example. If you're reading it as a reference implementation, those two files are the whole package.
If you inherit a Symfony2 app (the only case where it's usable): composer require --dev jmikola/insecure-routes-bundle, register it in AppKernel::registerBundles() inside the dev/test branch, enable it in config_dev.yml, bin/console cache:clear, and confirm an HTTPS-required route now serves over HTTP locally.
Laravel first use case: you added URL::forceScheme('https') to AppServiceProvider::boot() and now every generated URL and redirect points at a certificate your laptop doesn't have. The minimal, correct fix:
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
}
That one conditional covers ~90% of what the bundle does, in your own codebase, with no dependency.
The Symfony2 pattern the bundle enforces is coarse and environment-scoped: the environment is the security boundary. Enable the bundle in dev/test, and scheme enforcement disappears for that environment globally. There is no per-route opt-in or opt-out. That bluntness is a feature — it makes "is HTTPS weakened here?" a question you answer by looking at one kernel file.
Carry that discipline into Laravel. Ordered by preference:
Don't disable HTTPS locally — enable it. valet secure, herd secure, or mkcert gives you a trusted local certificate. This exercises the same code path production uses and eliminates the entire problem class. It is strictly better than weakening scheme enforcement.
Environment-gated forceScheme (above), kept in AppServiceProvider::boot() and keyed off app()->environment(...) rather than env('APP_ENV'), so it survives config:cache.
Environment-gated redirect middleware for legacy routes that must hard-redirect:
final class ForceHttps
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->secure() && app()->environment('production')) {
return redirect()->secure($request->getRequestUri(), 301);
}
return $next($request);
}
}
Route::middleware('force.https')->group(function () {
Route::get('/account', AccountController::class);
});
Integration tips that transfer from the bundle's design:
if (local) { http } through controllers is precisely the mess the Symfony bundle exists to prevent.URL::forceScheme('https') is active under the production environment and that HTTPS-required routes redirect — a failing test is the guardrail the package never had.TrustProxies as part of the security surface, not a workaround. Behind a TLS-terminating load balancer, the app sees HTTP and needs forwarded-header trust, not scheme stripping. Configure TrustProxies correctly and let the framework infer the scheme.Package-level pitfalls:
Laravel-side gotchas:
env() in application code breaks under config:cache. Use app()->environment(), which reads from the cached config. A env('APP_ENV') === 'local' check silently returns null in a cached deployment.TrustProxies and correct X-Forwarded-Proto handling — not relaxation. Using an insecure-routes approach here means disabling a control in the environment that needs it most.SESSION_SECURE_COOKIE, Secure cookie flags, and HSTS headers all key off the perceived scheme. If you gate HTTPS relaxation inconsistently across provider boot, middleware, and cookie config, you get cookies dropped in one environment and plaintext sessions in another. Keep them governed by the same environment check.http://localhost. $request->secure() is false unless you pass an absolute URL: $this->get('https://localhost/account'). This bites when asserting redirect targets or secure cookies.AppServiceProvider boot branch and a single middleware class, with a test asserting production behavior. That's the whole extension surface, and keeping it small is the point.How can I help you explore Laravel packages today?