Laravel · HTTP · Requests · Middleware

Middleware en Laravel: qué es, cómo crearlo y cuándo usarlo

Un middleware en Laravel es una capa que puede inspeccionar, modificar, permitir o detener una solicitud HTTP antes de que llegue a la lógica principal de la aplicación. Se utiliza para problemas transversales como autenticación, verificación, rate limiting, condiciones de acceso y procesamiento alrededor del request, pero no debería convertirse en un lugar donde almacenar toda la lógica de negocio.

Request → Middleware → Controller Aliases · Groups Parameters · Priority
Respuesta rápida

¿Qué es un middleware en Laravel?

Un middleware es una capa situada entre la petición HTTP y la lógica que finalmente procesa esa petición.

Puede examinar el usuario autenticado, headers, parámetros, estado de la cuenta u otras características del request.

Si la solicitud cumple las condiciones, el middleware la entrega a la siguiente capa mediante:

Middleware Laravel
return $next($request);

Si la condición no se cumple, puede devolver una respuesta inmediatamente y evitar que el controller llegue a ejecutarse.

Publicidad
Dónde encaja

El middleware forma parte del recorrido de una solicitud

01

Request

Llega una solicitud HTTP.

02

Middleware

Inspecciona o filtra la solicitud.

03

Route

Laravel determina el destino correspondiente.

04

Controller

Coordina la operación principal.

05

Response

Se genera una respuesta HTTP.

06

Salida

La respuesta atraviesa nuevamente las capas correspondientes.

Publicidad
Ciclo HTTP

Piensa en middleware como capas alrededor de la aplicación

Imagina una solicitud:

HTTP Request
GET /panel

Antes de ejecutar el controller, la solicitud puede atravesar varias capas.

Pipeline Laravel
Request
   ↓
Middleware A
   ↓
Middleware B
   ↓
Middleware C
   ↓
Route
   ↓
Controller
   ↓
Response

Cada middleware puede permitir que la petición continúe o interrumpir el recorrido.

Ejemplo: usuario no autenticado

Flujo Auth
GET /panel
   ↓
auth
   ↓
¿Usuario autenticado?
   ↓
NO
   ↓
No ejecutar PanelController

Esa capacidad evita repetir la misma comprobación dentro de cada controller.

Casos de uso

¿Para qué sirve un middleware en Laravel?

Middleware resulta útil cuando una regla está relacionada con el tratamiento de muchas solicitudes, no únicamente con una operación específica del negocio.

01

Exigir autenticación.

02

Comprobar que un email esté verificado.

03

Aplicar rate limiting.

04

Comprobar determinadas condiciones de acceso.

05

Añadir información a una respuesta.

06

Registrar datos relacionados con requests.

Middleware no debería resolver cualquier problema

Que técnicamente puedas ejecutar lógica compleja dentro de una capa no significa que debas hacerlo.

Crear facturas, procesar pagos o calcular todo un pedido no son buenas responsabilidades para un middleware.
Crear middleware

Genera tu primer middleware personalizado

Artisan puede crear la estructura inicial.

Terminal Artisan
php artisan make:middleware EnsureAccountIsActive

La clase quedará normalmente dentro de:

Archivo Laravel
app/Http/Middleware/EnsureAccountIsActive.php

Podemos comprobar si la cuenta del usuario está activa.

EnsureAccountIsActive.php Middleware
<?php

namespace App\Http\Middleware;

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

final class EnsureAccountIsActive
{
    public function handle(
        Request $request,
        Closure $next
    ): Response {

        $user = $request->user();

        if (
            $user !== null
            &&
            ! $user->activo
        ) {
            abort(
                403,
                'La cuenta está desactivada.'
            );
        }

        return $next($request);
    }
}

El middleware comprueba una condición y, si puede continuar, entrega el request a la siguiente capa.

Si esta ruta siempre requiere usuario autenticado, normalmente conviene ejecutar auth antes de depender de $request->user().
Publicidad
$next

$next($request) es lo que permite que la solicitud siga avanzando

Este fragmento:

Pipeline Continue
return $next($request);

entrega la solicitud a la siguiente capa del pipeline.

En cambio:

Pipeline Stop
return response()->json(
    [
        'message' =>
            'No tienes acceso.'
    ],
    403
);

termina el recorrido en ese punto.

Esa diferencia es la esencia del middleware

Puede actuar como una puerta:

Concepto Middleware
Condición correcta
      ↓
$next($request)
      ↓
Continúa

Condición incorrecta
      ↓
return Response
      ↓
Se detiene
Asignación

Puedes asignar una clase middleware directamente a una ruta

routes/web.php Middleware
<?php

use App\Http\Middleware\EnsureAccountIsActive;
use Illuminate\Support\Facades\Route;

Route::get(
    '/panel',
    [PanelController::class, 'index']
)->middleware(
    EnsureAccountIsActive::class
);

También puedes asignar varias capas a la misma ruta.

Route Multiple middleware
Route::get(
    '/panel',
    [PanelController::class, 'index']
)->middleware([
    'auth',
    EnsureAccountIsActive::class
]);

Aquí primero necesitamos una identidad autenticada y después comprobamos el estado de esa cuenta.

Publicidad
Aliases

Un alias evita repetir el nombre completo de una clase

Si utilizas frecuentemente un middleware, puedes registrarle un nombre corto desde:

Archivo Laravel
bootstrap/app.php
bootstrap/app.php Alias
<?php

use App\Http\Middleware\EnsureAccountIsActive;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(
    function (Middleware $middleware): void {

        $middleware->alias([
            'account.active' =>
                EnsureAccountIsActive::class
        ]);

    }
)

Después la ruta resulta mucho más compacta.

Route Alias
Route::get(
    '/panel',
    [PanelController::class, 'index']
)->middleware([
    'auth',
    'account.active'
]);

El alias debería comunicar claramente qué condición representa.

Middleware incluidos

Laravel ya proporciona aliases para necesidades frecuentes

Alias Objetivo
auth Exigir autenticación
guest Trabajar con usuarios no autenticados
can Aplicar autorización
verified Exigir verificación de email
signed Validar URLs firmadas
throttle Limitar solicitudes
password.confirm Exigir confirmación reciente de contraseña

Esto significa que no deberías recrear manualmente capacidades que Laravel ya proporciona de manera coherente.

auth middleware

auth responde a una pregunta sencilla: ¿hay una identidad autenticada?

Route auth
Route::get(
    '/perfil',
    [PerfilController::class, 'show']
)->middleware('auth');

No deberías repetir dentro del controller:

Evitar Repetición
if (! auth()->check()) {
    // ...
}

en cada método privado de la aplicación.

Middleware permite expresar esa condición en el límite HTTP.

Autenticación

Aprende sesiones, Sanctum, Policies y permisos

Middleware auth es solo una parte del sistema de acceso.

Autenticación en Laravel →
can middleware

El middleware can conecta el ciclo HTTP con las reglas de autorización

Supongamos que una ruta actualiza un proyecto.

Authorization Route
Route::patch(
    '/proyectos/{proyecto}',
    [ProyectoController::class, 'update']
)->middleware(
    'can:update,proyecto'
);

Antes de ejecutar el controller, Laravel puede comprobar si el usuario está autorizado para realizar update sobre ese proyecto.

Esto enlaza middleware con Policies o las reglas de autorización correspondientes.

No conviertas un middleware personalizado de roles en sustituto de toda tu autorización por recurso.

“Es administrador” y “puede modificar este registro” no siempre significan lo mismo.

Publicidad
Route groups

Agrupa rutas cuando comparten las mismas condiciones

Imagina un panel con veinte rutas privadas.

No necesitas añadir:

Repetición Evitar
->middleware([
    'auth',
    'account.active'
]);

veinte veces.

Puedes agruparlas.

Routes Group
Route::middleware([
    'auth',
    'account.active'
])->group(function () {

    Route::get(
        '/panel',
        [PanelController::class, 'index']
    );

    Route::get(
        '/perfil',
        [PerfilController::class, 'show']
    );

    Route::get(
        '/pedidos',
        [PedidoController::class, 'index']
    );

});

La agrupación comunica que todas esas rutas comparten las mismas condiciones de acceso.

Middleware groups

También puedes crear un grupo reutilizable de middleware

Si la combinación se utiliza en varias partes, puedes crear un grupo.

bootstrap/app.php Group
<?php

use App\Http\Middleware\EnsureAccountIsActive;
use App\Http\Middleware\EnsureCompanyIsSelected;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(
    function (Middleware $middleware): void {

        $middleware->appendToGroup(
            'account',
            [
                EnsureAccountIsActive::class,
                EnsureCompanyIsSelected::class
            ]
        );

    }
)

Después:

Route Group alias
Route::middleware([
    'auth',
    'account'
])->group(function () {

    // Rutas privadas.

});

Esto puede resultar útil cuando varias capas representan una preocupación transversal común.

web y api

Los grupos web y api tienen responsabilidades diferentes

Las rutas web necesitan normalmente infraestructura relacionada con:

01

Cookies.

02

Sesiones.

03

Errores compartidos con vistas.

04

Protección de formularios.

05

Route model binding.

Las rutas de una API pueden tener necesidades distintas.

Laravel administra grupos predeterminados para esos contextos.

No elimines o reemplaces middleware del grupo web simplemente porque no entiendes todavía por qué está allí.

Cookies, sesiones y protección de requests dependen del pipeline.

API

El middleware cobra especial importancia en APIs autenticadas

auth:sanctum, autorización y límites de solicitudes.

Crear API REST con Laravel →
Publicidad
Middleware global

Un middleware global participa en todas las solicitudes HTTP

Eso lo convierte en una herramienta poderosa, pero también en una decisión que deberías tomar con cuidado.

Puedes añadir un middleware al stack global desde:

bootstrap/app.php Global
<?php

use App\Http\Middleware\AddRequestId;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(
    function (Middleware $middleware): void {

        $middleware->append(
            AddRequestId::class
        );

    }
)

¿Cuándo tiene sentido?

Cuando la preocupación verdaderamente afecta a prácticamente todos los requests.

¿Cuándo no?

Cuando una condición solo pertenece a tres rutas concretas.

Hacer global un middleware innecesario significa ejecutar trabajo innecesario en cada request.
Antes y después

Un middleware puede ejecutar lógica tanto antes como después del controller

Antes

Before middleware Laravel
<?php

public function handle(
    Request $request,
    Closure $next
): Response {

    // Trabajo antes.

    return $next($request);
}

Después

After middleware Laravel
<?php

public function handle(
    Request $request,
    Closure $next
): Response {

    $response = $next($request);

    // Trabajo después.

    return $response;
}

Observa la diferencia:

Pipeline Before / After
Middleware
    ↓
trabajo antes
    ↓
$next($request)
    ↓
Controller
    ↓
Response
    ↓
trabajo después
    ↓
Cliente

Esta estructura permite implementar preocupaciones relacionadas con entrada y salida del ciclo HTTP.

Response

Un middleware también puede modificar la respuesta

Por ejemplo, puedes añadir un header relacionado con trazabilidad.

Response header Middleware
<?php

public function handle(
    Request $request,
    Closure $next
): Response {

    $response = $next($request);

    $response->headers->set(
        'X-App-Version',
        '1'
    );

    return $response;
}

Nuevamente, la cuestión importante es si esa tarea realmente pertenece a una preocupación transversal del ciclo HTTP.

Publicidad
Parámetros

Un middleware puede recibir parámetros desde la ruta

Imagina una condición relacionada con un rol.

EnsureUserHasRole.php Parameter
<?php

namespace App\Http\Middleware;

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

final class EnsureUserHasRole
{
    public function handle(
        Request $request,
        Closure $next,
        string $role
    ): Response {

        $user = $request->user();

        if (
            $user === null
            ||
            $user->role !== $role
        ) {
            abort(403);
        }

        return $next($request);
    }
}

Registramos un alias:

bootstrap/app.php Alias
$middleware->alias([
    'role' => EnsureUserHasRole::class
]);

Y la ruta puede indicar el parámetro.

Route Parameter
Route::get(
    '/administracion',
    [AdminController::class, 'index']
)->middleware([
    'auth',
    'role:admin'
]);

También pueden existir múltiples parámetros

Syntax Parameters
middleware:valor1,valor2
Este ejemplo enseña parámetros; para autorización compleja por recurso siguen siendo preferibles Policies y reglas centralizadas.
Rate limiting

throttle es otro buen ejemplo de una preocupación transversal

Un endpoint no debería necesitar implementar manualmente lógica repetida para contar solicitudes.

Puedes aplicar límites mediante middleware según la estrategia de la aplicación.

Route Throttle
Route::post(
    '/contacto',
    [ContactoController::class, 'store']
)->middleware(
    'throttle:10,1'
);

Conceptualmente, una regla como esta limita el volumen aceptado dentro de una ventana determinada.

No todas las rutas deberían tener el mismo límite

Generar IA, intentar login, leer un catálogo y crear un pago tienen perfiles de riesgo y costo completamente diferentes.

Rate limiting es una capa defensiva, no una sustitución de autenticación, autorización o validación.
APIs

auth:sanctum es middleware, pero Sanctum hace mucho más que una sola comprobación

En una API protegida puedes utilizar:

Route Sanctum
Route::get(
    '/usuario',
    function (Request $request) {

        return $request->user();

    }
)->middleware(
    'auth:sanctum'
);

El middleware determina si existe una identidad aceptada por ese guard.

Después siguen existiendo otras preguntas:

01

¿Puede leer este recurso?

02

¿Pertenece a esta empresa?

03

¿Puede modificar este registro?

04

¿Tiene la capacidad necesaria?

Autenticación y Sanctum →
Publicidad
Orden

El orden del middleware puede cambiar el resultado

Imagina dos capas:

Orden Middleware
auth
↓
account.active

El segundo middleware presupone que puede trabajar con un usuario.

Ejecutarlo antes de establecer correctamente la autenticación podría cambiar su comportamiento.

En casos avanzados existe prioridad de middleware

Laravel permite controlar el orden relativo cuando realmente es necesario.

bootstrap/app.php Priority
$middleware->priority([
    FirstMiddleware::class,
    SecondMiddleware::class
]);
No configures prioridad personalizada como primera solución a cualquier problema.

Si dos capas están excesivamente acopladas, quizá exista un problema de diseño más profundo.

Excepciones

Puedes excluir middleware de rutas concretas dentro de determinados grupos

Imagina un grupo donde casi todas las páginas necesitan una condición, excepto una.

Routes withoutMiddleware
Route::middleware([
    EnsureAccountIsActive::class
])->group(function () {

    Route::get(
        '/panel',
        [PanelController::class, 'index']
    );

    Route::get(
        '/cuenta-bloqueada',
        [AccountController::class, 'blocked']
    )->withoutMiddleware([
        EnsureAccountIsActive::class
    ]);

});

Tiene sentido:

un usuario con cuenta bloqueada necesita poder ver precisamente la página que explica por qué está bloqueada.

withoutMiddleware elimina middleware de ruta; no debería entenderse como una forma general de saltarse cualquier protección global.
Middleware o controller

¿Cómo decidir si una regla pertenece a middleware?

Hazte una pregunta:

¿Esta regla existe porque estamos procesando una solicitud HTTP o porque el negocio necesita realizar una operación concreta?

Buen candidato a middleware

Ejemplos Transversal
¿Está autenticado?
¿Está verificado?
¿Está activa la cuenta?
¿Excedió el límite de requests?
¿Puede entrar a esta zona?

Mal candidato

Ejemplos Negocio
Calcular total del pedido.
Crear una factura.
Aplicar descuentos.
Reservar inventario.
Procesar un pago.

Esas últimas operaciones representan casos de uso del negocio.

Meterlas dentro de middleware puede esconder el verdadero flujo de la aplicación.

Terminable middleware

Determinado trabajo puede ejecutarse después de enviar la respuesta

Laravel admite middleware con un método terminate().

Middleware terminate()
<?php

final class LogRequestMetrics
{
    public function handle(
        Request $request,
        Closure $next
    ): Response {

        return $next($request);
    }


    public function terminate(
        Request $request,
        Response $response
    ): void {

        // Trabajo posterior
        // a la respuesta.

    }
}

Puede resultar útil para determinadas tareas posteriores al envío de la respuesta, dependiendo del servidor y del tipo de trabajo.

“Después de responder” no convierte terminate() en una cola de trabajos.

Para procesos pesados, reintentos, trabajos demorados o ejecución distribuida, una queue suele representar otro problema arquitectónico.

Rendimiento

Cada middleware añade trabajo al request

Una comprobación sencilla puede tener un costo mínimo.

Pero imagina un middleware que en cada request:

01

Ejecuta cinco consultas.

02

Consulta una API externa.

03

Carga relaciones enormes.

04

Procesa archivos.

Si además es global, ese costo se replica en toda la aplicación.

Middleware debería ser suficientemente ligero para justificar ejecutarse en cada request al que está asignado.
Laravel y rendimiento de consultas →
Dependency Injection

Un middleware también puede recibir dependencias mediante el contenedor

No necesitas crear manualmente todas las clases que utiliza.

Constructor Dependency
<?php

final class EnsureSubscriptionIsActive
{
    public function __construct(
        private SubscriptionService $subscriptions
    ) {}


    public function handle(
        Request $request,
        Closure $next
    ): Response {

        if (
            ! $this->subscriptions
                ->isActive(
                    $request->user()
                )
        ) {
            abort(403);
        }

        return $next($request);
    }
}

Esto permite que la capa HTTP delegue una comprobación especializada.

Pero cuida la dirección de dependencias

Si el middleware comienza a coordinar toda la aplicación, probablemente haya dejado de ser solamente una capa de entrada.

POO e inyección de dependencias →
Testing

Prueba el comportamiento observable, no solamente la clase middleware

Si una ruta exige una cuenta activa, una prueba importante es comprobar que una cuenta inactiva no puede acceder.

HTTP Test Middleware
<?php

public function test_usuario_inactivo_no_puede_entrar_al_panel(): void
{
    $user = User::factory()
        ->create([
            'activo' => false
        ]);

    $this
        ->actingAs($user)
        ->get('/panel')
        ->assertForbidden();
}

Y también deberías comprobar el camino permitido.

HTTP Test Allowed
<?php

public function test_usuario_activo_puede_entrar_al_panel(): void
{
    $user = User::factory()
        ->create([
            'activo' => true
        ]);

    $this
        ->actingAs($user)
        ->get('/panel')
        ->assertOk();
}

Esto verifica el comportamiento que realmente importa al consumidor de la aplicación.

Publicidad
Decisión

¿Middleware, Policy, Form Request, controller o servicio?

Necesidad Lugar habitual
Exigir autenticación Middleware
Rate limiting Middleware
Validar datos del request Form Request
Autorizar modificación de un model Policy
Coordinar HTTP Controller
Ejecutar proceso de negocio Servicio / caso de uso
Consultar persistencia Eloquent / capa de datos

Estas fronteras no son leyes universales, pero ayudan a evitar que cada clase termine haciendo de todo.

Errores frecuentes

Los errores más comunes al usar middleware en Laravel

Error Lógica de negocio enorme
Separa
Error Consultas costosas en cada request
Optimiza
Error Convertir todo en middleware global
Limita
Error Confundir auth con autorización
Policies
Error Ignorar el orden
Pipeline
Error Duplicar condiciones por rutas
Agrupa
Error Crear aliases poco claros
Nombra bien
Error No probar rechazo
Testea
Ruta de aprendizaje

En qué orden aprender middleware en Laravel

1

HTTP

Request y response.

2

Pipeline

Comprende $next().

3

auth

Observa un middleware real.

4

Personalizado

Crea una condición propia.

5

Aliases

Simplifica rutas.

6

Groups

Reutiliza conjuntos.

7

Parameters

Configura comportamiento.

8

Before / after

Comprende el recorrido.

9

Prioridad

Estudia dependencias entre capas.

10

Testing

Prueba acceso y rechazo.

Conclusión

Middleware funciona mejor cuando protege el límite HTTP sin esconder el negocio

Utiliza middleware para preocupaciones transversales de requests y responses.

Comprende qué hace $next($request) antes de memorizar sintaxis.

Aplica middleware solamente a las rutas que realmente lo necesitan.

Utiliza aliases cuando mejoren la claridad.

Agrupa middleware cuando varias rutas comparten condiciones.

Aprende los grupos web y api.

Considera el orden cuando una capa depende de otra.

No conviertas middleware en un servicio de negocio disfrazado.

Utiliza Policies para autorización específica sobre recursos.

Prueba tanto los accesos permitidos como los denegados.

Un buen middleware hace visible una condición transversal del request; un mal middleware esconde procesos importantes del negocio en un lugar donde nadie espera encontrarlos.

Cluster Laravel

Continúa profundizando en Laravel

Publicidad
Preguntas frecuentes

Preguntas sobre middleware en Laravel

Es una capa del ciclo HTTP que puede inspeccionar, modificar, permitir o detener una solicitud antes de que continúe hacia otras capas de la aplicación.

Puedes generarlo con el comando php artisan make:middleware. La clase creada contiene un método handle que recibe el Request, el callback $next y opcionalmente parámetros adicionales.

Entrega la solicitud a la siguiente capa del pipeline. Si el middleware devuelve otra respuesta antes de ejecutar $next, la solicitud puede detenerse en ese punto.

Puedes asignar directamente la clase a una ruta. Cuando necesitas aliases, middleware globales, grupos o configuraciones relacionadas, Laravel permite gestionarlos desde bootstrap/app.php.

Middleware resulta adecuado para preocupaciones transversales del request, como exigir autenticación. Una Policy organiza reglas de autorización relacionadas con acciones sobre un model o recurso concreto.

auth es un alias del middleware de autenticación incluido en Laravel. Se utiliza para exigir una identidad autenticada antes de permitir que una ruta protegida continúe.

Sí. Los parámetros adicionales se entregan al método handle después de $next. Esto permite reutilizar el mismo middleware con valores distintos según la ruta.

Es un middleware incorporado al stack global de la aplicación, por lo que participa en todas las solicitudes HTTP. Solo debería utilizarse cuando la preocupación realmente sea global.

Sí. Un middleware puede obtener primero la respuesta mediante $next($request), ejecutar lógica después y finalmente devolver esa respuesta. Laravel también admite middleware terminables para determinados trabajos posteriores.

Publicidad
Comprende el pipeline

Middleware deja de parecer magia cuando entiendes por dónde viaja una solicitud

Aprende primero request y response, después $next(), middleware de ruta, aliases, grupos y parámetros. Finalmente profundiza en autenticación, autorización, APIs y testing. Así sabrás no solamente cómo crear middleware, sino cuándo realmente corresponde utilizarlo.

Volver al hub Laravel →
Carrito de compra
Scroll al inicio