Laravel

10 consejos para mejorar el rendimiento de tus aplicaciones Laravel

Una aplicación Laravel lenta casi nunca se arregla con un servidor más grande. Diez cambios, de lo más barato a lo más estructural, para que vaya más rápida.

Una aplicación Laravel lenta casi nunca se arregla con un servidor más grande. En la mayoría de proyectos que reviso, el tiempo se va en unas pocas consultas mal planteadas, en trabajo que podría hacerse en segundo plano o en cachés que nadie ha activado. Estos son los diez cambios que más impacto suelen tener, ordenados de lo más barato a lo más estructural.

1. Mide antes de optimizar

Optimizar sin datos es adivinar. Antes de tocar código, localiza dónde se va el tiempo de verdad:

  • En local: Laravel Debugbar o Telescope para ver cuántas consultas lanza cada petición y cuánto tarda cada una.
  • En producción: Laravel Pulse para detectar rutas lentas, consultas lentas y colas atascadas.
  • En la base de datos: el slow query log de MySQL con un umbral razonable (por ejemplo, 500 ms).

Apunta el tiempo de respuesta y el número de consultas de las tres o cuatro rutas más usadas. Ese será tu punto de partida.

2. Elimina las consultas N+1

Es el problema más habitual: recorrer una colección y acceder a una relación en cada vuelta provoca una consulta por elemento. Carga las relaciones por adelantado con with():

// Antes: 1 consulta para pedidos + 1 por cada cliente
$pedidos = Pedido::latest()->take(50)->get();

// Después: 2 consultas en total
$pedidos = Pedido::with('cliente')->latest()->take(50)->get();

Para que no vuelva a pasar, haz que Laravel avise en desarrollo cuando se produce una carga perezosa:

// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(! app()->isProduction());
}

3. Pide solo las columnas que necesitas e indexa bien

Un select * sobre tablas con campos de texto largos o JSON mueve mucha más información de la necesaria. Limita las columnas con select() y revisa que las columnas que usas en where, orderBy y en las claves de relación tienen índice. Un EXPLAIN sobre la consulta lenta suele bastar para ver si MySQL está recorriendo la tabla entera.

4. Pagina y procesa por lotes

Nunca cargues una tabla completa en memoria. Para listados, usa paginate() o cursorPaginate(). Para procesos masivos (exportaciones, recálculos, importaciones), trabaja por bloques:

Pedido::where('estado', 'pendiente')
    ->chunkById(500, function ($pedidos) {
        foreach ($pedidos as $pedido) {
            $pedido->recalcularTotales();
        }
    });

5. Mueve el trabajo pesado a colas

Enviar emails, generar PDFs, llamar a APIs externas o procesar imágenes no debería hacer esperar al usuario. Implementa ShouldQueue en esos trabajos, usa Redis como driver de colas y supervisa los workers con Horizon. La petición responde al momento y el trabajo se hace en segundo plano, con reintentos si algo falla.

6. Cachea los datos que cambian poco

Catálogos, configuraciones, contadores y resultados de consultas costosas son candidatos claros. Cache::remember() resuelve la mayoría de casos, y Cache::flexible() permite servir el valor anterior mientras se recalcula en segundo plano:

$categorias = Cache::remember('categorias.menu', now()->addHour(), function () {
    return Categoria::orderBy('orden')->get(['id', 'nombre', 'slug']);
});

Lo importante no es cachear mucho, sino saber cuándo invalidar: asocia cada clave a un evento del modelo que la cambia.

7. Activa las cachés del framework en cada despliegue

Laravel puede precompilar configuración, rutas, vistas y eventos. En producción, añade este paso a tu script de despliegue:

composer install --no-dev --optimize-autoloader
php artisan optimize

Recuerda que, con la configuración cacheada, env() solo debe usarse dentro de los ficheros de config/.

8. Ajusta PHP y el servidor

OPcache activado y bien dimensionado es obligatorio en producción. A partir de ahí, revisa los procesos de PHP-FPM según la memoria disponible, activa la compresión (gzip o Brotli) en Nginx y sirve los assets compilados con Vite con cabeceras de caché largas. Si la aplicación tiene mucho tráfico, Laravel Octane mantiene la aplicación en memoria entre peticiones y reduce mucho el tiempo de arranque.

9. Devuelve respuestas más ligeras

En APIs, usa API Resources para devolver solo los campos que el cliente necesita, y evita serializar relaciones completas por defecto. En vistas Blade, no lances consultas desde la plantilla: prepara los datos en el controlador o en un view model.

10. Protege las mejoras con pruebas

Una optimización sin prueba se pierde en el siguiente sprint. Añade tests que fallen si una ruta crítica vuelve a disparar consultas de más:

public function test_listado_de_pedidos_no_tiene_n_mas_1(): void
{
    Pedido::factory()->count(30)->create();

    $this->expectsDatabaseQueryCount(3);

    $this->actingAs(User::factory()->create())
        ->get('/pedidos')
        ->assertOk();
}

Y después de cada despliegue, compara los tiempos con tu punto de partida.

Por dónde empezar

Si solo puedes hacer una cosa esta semana: mide las rutas más usadas, elimina los N+1 que aparezcan y mueve a una cola el proceso más lento. En la mayoría de aplicaciones, esos tres cambios ya se notan.

Si tu aplicación Laravel va lenta y no tienes claro por dónde empezar, puedo hacer una revisión de rendimiento de tu proyecto Laravel con un informe priorizado. También puedes ver cómo aplico estas prácticas en casos reales como el portal de clientes B2B o la app de control de tareas de almacén.

¿Quieres aplicar estas ideas a tu proyecto?

Revisamos el contexto y definimos el siguiente paso.