Introducción
Laravel 13 se lanzó en marzo de 2026. Esta guía explica los pasos para actualizar de Laravel 12.x a 13.x.El tiempo estimado necesario para la actualización es de unos 10 minutos. Sin embargo, el impacto de los cambios disruptivos en tu aplicación varía según el tamaño y las funcionalidades utilizadas.
Actualización con IA
También puedes automatizar la actualización con Laravel Boost. Boost es un servidor MCP de primera parte que ofrece prompts de actualización paso a paso a los asistentes de IA. Tras instalarlo en una aplicación Laravel 12, puedes iniciar la actualización a Laravel 13 usando el slash command/upgrade-laravel-v13 en Claude Code, Cursor, OpenCode, Gemini o VS Code. Este comando requiere laravel/boost ^2.0.
También en herramientas de IA que no admiten slash commands puedes ejecutar el mismo procedimiento de actualización consultando directamente el archivo de prompt. Pega el siguiente prompt tal cual en la IA.
prompt
Cambios por nivel de impacto
Impacto: alto
- Actualización de dependencias
- Actualización del instalador de Laravel
- Prevención de request forgery (CSRF)
Impacto: medio
- Configuración
serializable_classesde caché - Configuración
serializationde sesión
Impacto: bajo
- Prefijo de caché y nombre de cookie de sesión
- Serialización de colecciones de modelos
Container::cally valores por defecto nullable de clase- Prioridad de registro de rutas por dominio
- Payload de excepción en el evento
JobAttempted - Binding del callback de
extenden Manager - Query
DELETEde MySQL (JOIN / ORDER BY / LIMIT) - Nombre de la vista de paginación de Bootstrap
- Generación del nombre de tablas pivote polimórficas
- Renombrado de propiedad en el evento
QueueBusy - Reset entre tests de la factoría
Str
Pasos de actualización
Actualización de dependencias
Impacto: alto Actualiza las siguientes dependencias encomposer.json.
Actualización del instalador de Laravel
Impacto: alto Si creas nuevas aplicaciones de Laravel con el CLI del instalador, actualízalo a una versión compatible con Laravel 13.x. Si lo instalaste concomposer global require:
Cambios disruptivos (Breaking Changes)
Seguridad
Prevención de request forgery
Impacto: alto El middleware CSRF de Laravel se ha renombrado deVerifyCsrfToken a PreventRequestForgery. Además, se ha añadido validación del origen de la petición usando la cabecera Sec-Fetch-Site.
VerifyCsrfToken y ValidateCsrfToken permanecen como alias obsoletos, pero todos los lugares que los referencien directamente deben actualizarse a PreventRequestForgery. Presta especial atención si excluyes middleware en tests o definiciones de rutas.
preventRequestForgery(...).
Caché
Prefijo de caché y nombre de cookie de sesión
Impacto: bajo Los prefijos por defecto de caché y de claves Redis en Laravel ahora usan sufijos separados por guiones. Además, el nombre por defecto de la cookie de sesión pasa a usarStr::snake(...).
Muchas aplicaciones configuran estos valores explícitamente en los archivos de configuración, por lo que este cambio no las afecta. Solo se ven afectadas las aplicaciones que dependen de la configuración de fallback del framework.
.env.
Configuración serializable_classes de caché
Impacto: medio
Se ha añadido la opción serializable_classes en la configuración por defecto de cache, con valor predeterminado false. Con ello se previenen los ataques de deserialization gadget chain de PHP en caso de fuga de APP_KEY.
Si tu aplicación guarda intencionadamente objetos PHP en caché, hay que listar explícitamente las clases cuya deserialización se permite.
Configuración serialization de sesión
Impacto: medio
En config/session.php del esqueleto de Laravel 13 (laravel/laravel) se ha añadido 'serialization' => 'json'. Sin embargo, el valor por defecto interno del framework sigue siendo php.
La guía oficial de actualización no menciona el cambio de este ajuste. Esto se interpreta como que no hace falta cambiarlo. En Laravel, en actualizaciones a la versión inmediatamente anterior, a veces hay cambios que no se documentan en la guía de actualización porque no tienen impacto. Cuando se intenta actualizar años después y no hay información, uno se encuentra con problemas; por eso es importante dejar constancia extraoficial.
Para mantener el mismo comportamiento que hasta Laravel 12, indica explícitamente 'serialization' => 'php'.
Contenedor
Container::call y valores por defecto nullable de clase
Impacto: bajo
Container::call respeta ahora los valores por defecto de parámetros nullable de clase cuando no hay binding (coincide con el comportamiento introducido en Laravel 12 para la inyección de constructor).
Base de datos
Query DELETE de MySQL
Impacto: bajo
Laravel ahora compila queries DELETE ... JOIN completas con ORDER BY y LIMIT en la gramática de MySQL.
En versiones anteriores, en un DELETE con JOIN podían ignorarse las cláusulas ORDER BY / LIMIT. En Laravel 13 esas cláusulas se incluyen en el SQL generado. Como resultado, en algunos motores de base de datos que no soportan esta sintaxis puede producirse una QueryException.
Eloquent
Generación del nombre de tablas pivote polimórficas
Impacto: bajo Cuando se infiere el nombre de tabla de un modelo pivote polimórfico que usa una clase de pivote personalizada, Laravel ahora genera el nombre en plural. Si dependías del nombre singular anterior, indica explícitamente el nombre de la tabla en el modelo pivote.Serialización de colecciones de modelos
Impacto: bajo Al serializar y restaurar colecciones de modelos Eloquent (por ejemplo en jobs encolados), las relaciones eager cargadas se restauran ahora en los modelos. Si tenías código que dependía de que las relaciones no existieran tras la deserialización, tendrás que corregirlo.Cola
Payload de excepción en el evento JobAttempted
Impacto: bajo
En lugar de la anterior propiedad booleana $exceptionOccurred, el evento Illuminate\Queue\Events\JobAttempted expone ahora en $exception el objeto excepción (o null).
Renombrado de propiedad en el evento QueueBusy
Impacto: bajo
La propiedad $connection del evento Illuminate\Queue\Events\QueueBusy se ha renombrado a $connectionName por coherencia con otros eventos de cola.
Enrutamiento
Prioridad de registro de rutas por dominio
Impacto: bajo Las rutas con un dominio explícito tienen ahora prioridad frente a las rutas sin dominio en el matching. Con esto, las rutas de subdominio catch-all funcionan de forma coherente aunque se hayan registrado antes rutas sin dominio.Soporte
Binding del callback de extend en Manager
Impacto: bajo
Los closures de drivers personalizados registrados con el método extend de Manager se enlazan ahora a la instancia del manager.
Antes, si en esos callbacks se estaba referenciando otro objeto (por ejemplo la instancia de service provider) como $this, tendrás que mover el valor a captura de closure con use (...).
Reset entre tests de la factoría Str
Impacto: bajo
Laravel ahora resetea las factorías Str personalizadas en el teardown de los tests.
Si dependías de que las factorías personalizadas de UUID / ULID / string aleatoria persistieran entre métodos de test, configúralas en cada test relacionado o en un hook de setup.
Vistas
Nombre de la vista de paginación de Bootstrap
Impacto: bajo Los nombres internos de las vistas de paginación por defecto de Bootstrap 3 son ahora explícitos.Funcionalidades obsoletas
| Funcionalidad | Alternativa |
|---|---|
Middleware VerifyCsrfToken | PreventRequestForgery |
Middleware ValidateCsrfToken | PreventRequestForgery |
JobAttempted::$exceptionOccurred | JobAttempted::$exception |
QueueBusy::$connection | QueueBusy::$connectionName |
Añadidos a contratos
Impacto: muy bajo Solo afecta si tienes implementaciones personalizadas.Contract Dispatcher
Se ha añadido el método dispatchAfterResponse($command, $handler = null) al contract Illuminate\Contracts\Bus\Dispatcher.
Contract ResponseFactory
Se ha añadido la firma eventStream al contract Illuminate\Contracts\Routing\ResponseFactory.
Contract MustVerifyEmail
Se ha añadido markEmailAsUnverified() al contract Illuminate\Contracts\Auth\MustVerifyEmail.
Contract Queue
Se han añadido al contract Illuminate\Contracts\Queue\Queue los siguientes métodos de inspección del tamaño de la cola (antes solo se declaraban en docblocks).
pendingSizedelayedSizereservedSizecreationTimeOfOldestPendingJob
Contracts Store / Repository
Se ha añadido el método touch para extender el TTL en los contracts de caché.
Puntos destacados de las novedades
Actualización asistida por IA (Laravel Boost)
Laravel Boost es un servidor MCP oficial. Se puede semi-automatizar la actualización con el comando/upgrade-laravel-v13, integrándose con los editores de IA.
Validación del origen de la petición con la cabecera Sec-Fetch-Site
El middleware PreventRequestForgery realiza una verificación adicional de origen usando la cabecera Sec-Fetch-Site. Con ello se refuerza la protección CSRF.
Deserialización segura de la caché
Con la configuraciónserializable_classes, solo se deserializan las clases permitidas. Mejora la seguridad frente a ataques de deserialización de PHP.
eventStream para SSE (Server-Sent Events)
Se ha añadido eventStream al contract ResponseFactory, mejorando el soporte de Server-Sent Events.
Mejora de la visibilidad de las colas
Con los métodospendingSize, delayedSize, reservedSize, etc. del contract Queue, es posible monitorizar con mayor detalle el estado de las colas.
Problemas frecuentes durante la migración y sus soluciones
Problema: los tests relacionados con CSRF fallan
Síntoma: los tests que referencianVerifyCsrfToken fallan con un error de clase no encontrada.
Solución: actualiza todas las referencias a PreventRequestForgery.
Problema: no se restauran objetos desde la caché
Síntoma: los datos obtenidos de la caché sonnull o se produce una UnserializationFailedException.
Solución: añade las clases usadas a serializable_classes en config/cache.php, o convierte los valores cacheados a array.
Problema: el listener JobAttempted no funciona
Síntoma: $event->exceptionOccurred es null o produce error de indefinido.
Solución: cámbialo a $event->exception !== null.
Problema: la sesión se invalida
Síntoma: tras la actualización, los usuarios son deslogueados. Solución: el nombre por defecto de la cookie de sesión ha cambiado. Establece explícitamenteSESSION_COOKIE en .env para mantener el valor anterior.
Problema: no se encuentran claves de caché
Síntoma: aumentan los cache miss tras la actualización. Solución: el prefijo de caché ha cambiado. EstableceCACHE_PREFIX en .env o limpia la caché.
Referencias
- Guía oficial de actualización (inglés)
- Diff del repositorio laravel/laravel (12.x → 13.x)
- Laravel Shift: servicio comunitario que automatiza la actualización
- Laravel Boost: servidor MCP para actualización asistida por IA