LoadForge LogoLoadForge

Laravel Load Testing Guide with LoadForge

Laravel Load Testing Guide with LoadForge

Introduction

Laravel is one of the most popular PHP web frameworks for building modern web applications, APIs, SaaS platforms, eCommerce stores, and internal business systems. Its elegant developer experience, rich ecosystem, and built-in features like routing, queues, caching, authentication, and Eloquent ORM make it a strong choice for teams that need to move quickly.

But developer productivity does not automatically guarantee production performance.

As traffic grows, Laravel applications can run into issues such as slow database queries, session lock contention, expensive middleware, queue backlogs, cache misses, and inefficient API serialization. That is why Laravel load testing is essential. With the right performance testing strategy, you can uncover bottlenecks before they impact users, validate that your application scales under real-world traffic, and confidently prepare for product launches, seasonal spikes, or enterprise onboarding events.

In this guide, you will learn how to load test Laravel apps with LoadForge using realistic Locust scripts. We will cover basic page load testing, authenticated API workflows, eCommerce-style scenarios, and file upload testing. Along the way, we will look at how Laravel behaves under load, how to analyze results, and how to optimize common pain points.

LoadForge makes this process easier with cloud-based infrastructure, distributed testing, global test locations, real-time reporting, and CI/CD integration, so you can run serious Laravel performance testing without maintaining your own load generation environment.

Prerequisites

Before you begin load testing Laravel with LoadForge, make sure you have the following:

  • A running Laravel application in a staging or performance environment
  • A list of important routes, APIs, and user journeys to test
  • Test accounts for authenticated scenarios
  • Seeded database data that resembles production usage
  • Optional monitoring tools such as:
    • Laravel Telescope or Horizon
    • New Relic, Datadog, or Blackfire
    • MySQL/PostgreSQL slow query logs
    • Redis metrics
    • Nginx, Apache, or PHP-FPM metrics

You should also know which Laravel stack you are testing, since behavior can differ depending on architecture:

  • Traditional Laravel web app with Blade views and session auth
  • Laravel API backend using Sanctum or Passport
  • Laravel app behind Nginx + PHP-FPM
  • Laravel Octane deployment using Swoole or RoadRunner
  • Queue-heavy app using Redis, Horizon, and background jobs

For effective load testing, use a realistic environment. Testing against a local development setup or a tiny shared server will not reflect production behavior. A staging environment that mirrors production infrastructure is ideal.

Understanding Laravel Under Load

Laravel handles incoming requests through a layered pipeline. Each request typically passes through web server handling, PHP-FPM or Octane workers, Laravel bootstrap, middleware, routing, controllers, Eloquent queries, cache access, session handling, and response rendering or JSON serialization.

Under load, common Laravel bottlenecks include:

Database Query Overload

Eloquent is powerful, but poorly optimized queries can become a major issue during stress testing. Common problems include:

  • N+1 query patterns
  • Missing indexes
  • Large pagination offsets
  • Unbounded relationship loading
  • Expensive aggregate queries

If a high-traffic endpoint triggers many ORM queries per request, your database often becomes the first scaling limit.

Session and Authentication Contention

Laravel apps using session-based authentication may experience lock contention if sessions are stored in files or if multiple concurrent requests are made for the same user. This is especially noticeable in traditional web apps with frequent AJAX calls.

Cache Misses and Inefficient Caching

Laravel often relies on Redis or Memcached for caching config, routes, sessions, rate limiting, and application data. During performance testing, ineffective cache strategy can produce spikes in database load and increased response times.

Queue Backlogs

Some Laravel endpoints dispatch jobs for emails, webhooks, report generation, or image processing. If background workers cannot keep up, the user-facing request may appear fast while downstream processing fails to scale.

PHP-FPM Worker Saturation

In traditional deployments, PHP-FPM can become saturated when too many concurrent requests arrive. Once all workers are busy, requests queue up and latency rises sharply. This is a classic issue uncovered by Laravel load testing.

Heavy Blade Rendering or API Serialization

Complex Blade templates, nested resources, and large JSON payloads can increase CPU usage. API responses that include many related models or computed attributes can become expensive under sustained traffic.

Understanding these behaviors helps you design realistic load tests that reflect how users actually interact with your Laravel application.

Writing Your First Load Test

A good first step is to test a few public endpoints such as the homepage, pricing page, login page, and a health or products endpoint. This gives you a baseline for response times and throughput before moving into authenticated workflows.

Here is a simple Locust script for a Laravel app with public web routes and a JSON API.

python
from locust import HttpUser, task, between
 
class LaravelPublicUser(HttpUser):
    wait_time = between(1, 3)
 
    @task(4)
    def homepage(self):
        self.client.get("/", name="GET /")
 
    @task(2)
    def pricing_page(self):
        self.client.get("/pricing", name="GET /pricing")
 
    @task(2)
    def login_page(self):
        self.client.get("/login", name="GET /login")
 
    @task(3)
    def products_api(self):
        self.client.get(
            "/api/products?category=electronics&page=1",
            headers={"Accept": "application/json"},
            name="GET /api/products"
        )
 
    @task(1)
    def product_detail(self):
        self.client.get(
            "/api/products/1254",
            headers={"Accept": "application/json"},
            name="GET /api/products/:id"
        )

What this test does

This script simulates anonymous users browsing a Laravel application. It mixes server-rendered pages and API traffic, which is common in Laravel apps that use Blade on the frontend and JSON APIs for dynamic components.

This test is useful for:

  • Establishing a baseline response time
  • Measuring how fast Laravel serves cached and uncached pages
  • Identifying slow public endpoints
  • Checking whether Nginx, PHP-FPM, and the database remain stable under moderate load

Why this is realistic for Laravel

Many Laravel apps expose both web routes and API routes. Public traffic often includes:

  • Landing pages
  • Login and registration screens
  • Product catalog browsing
  • Search and detail pages
  • Marketing and documentation pages

If these routes are slow under low or moderate concurrency, authenticated workflows will likely perform worse.

Advanced Load Testing Scenarios

Once you have a baseline, move on to realistic user behavior. For Laravel applications, that usually means authentication, session or token handling, CRUD operations, checkout flows, and uploads.

Scenario 1: Authenticated Laravel API Testing with Sanctum

Laravel Sanctum is widely used for SPA and API authentication. In this example, users log in, retrieve dashboard data, view orders, and update their profile.

python
from locust import HttpUser, task, between
 
class LaravelSanctumUser(HttpUser):
    wait_time = between(1, 2)
    token = None
 
    def on_start(self):
        response = self.client.post(
            "/api/login",
            json={
                "email": "loadtest.user1@example.com",
                "password": "Testing123!"
            },
            headers={
                "Accept": "application/json",
                "Content-Type": "application/json"
            },
            name="POST /api/login"
        )
 
        if response.status_code == 200:
            data = response.json()
            self.token = data.get("token")
 
    def auth_headers(self):
        return {
            "Accept": "application/json",
            "Authorization": f"Bearer {self.token}"
        }
 
    @task(3)
    def dashboard_summary(self):
        if self.token:
            self.client.get(
                "/api/dashboard/summary",
                headers=self.auth_headers(),
                name="GET /api/dashboard/summary"
            )
 
    @task(2)
    def list_orders(self):
        if self.token:
            self.client.get(
                "/api/orders?page=1&status=processing",
                headers=self.auth_headers(),
                name="GET /api/orders"
            )
 
    @task(1)
    def view_order(self):
        if self.token:
            self.client.get(
                "/api/orders/50128",
                headers=self.auth_headers(),
                name="GET /api/orders/:id"
            )
 
    @task(1)
    def update_profile(self):
        if self.token:
            self.client.put(
                "/api/user/profile",
                json={
                    "name": "Load Test User",
                    "timezone": "America/New_York",
                    "phone": "+1-555-010-2020"
                },
                headers={
                    **self.auth_headers(),
                    "Content-Type": "application/json"
                },
                name="PUT /api/user/profile"
            )

What this reveals

This type of Laravel performance testing helps identify issues in:

  • Authentication throughput
  • Token generation overhead
  • Middleware performance
  • User dashboard query efficiency
  • Order listing pagination
  • Profile update validation and database writes

If response times increase significantly after login, investigate auth middleware, database indexing, and N+1 queries in user-specific endpoints.

Scenario 2: Laravel eCommerce Workflow with Cart and Checkout

Many Laravel applications power online stores using custom builds or packages. This scenario simulates a realistic customer journey: browse category pages, view a product, add it to cart, and start checkout.

python
from locust import HttpUser, task, between, SequentialTaskSet
 
class ShopperJourney(SequentialTaskSet):
    def on_start(self):
        self.client.get("/", name="GET /")
        self.client.get("/category/laptops", name="GET /category/:slug")
 
    @task
    def view_product(self):
        self.client.get("/products/dell-xps-15", name="GET /products/:slug")
 
    @task
    def add_to_cart(self):
        self.client.post(
            "/cart",
            data={
                "product_id": "3421",
                "quantity": "1",
                "variant_id": "883"
            },
            name="POST /cart"
        )
 
    @task
    def view_cart(self):
        self.client.get("/cart", name="GET /cart")
 
    @task
    def apply_coupon(self):
        self.client.post(
            "/cart/coupon",
            data={"code": "SUMMER10"},
            name="POST /cart/coupon"
        )
 
    @task
    def begin_checkout(self):
        self.client.post(
            "/checkout",
            data={
                "email": "customer@example.com",
                "shipping_address": "123 Market Street",
                "shipping_city": "Austin",
                "shipping_state": "TX",
                "shipping_zip": "78701",
                "shipping_country": "US",
                "payment_method": "cod"
            },
            name="POST /checkout"
        )
 
class LaravelShopper(HttpUser):
    wait_time = between(2, 5)
    tasks = [ShopperJourney]

Why this matters

Laravel eCommerce flows often stress several systems at once:

  • Session storage
  • Cart persistence
  • Product inventory lookups
  • Coupon validation
  • Tax and shipping calculations
  • Order creation logic
  • Event dispatching and queued jobs

This is one of the best ways to uncover real business bottlenecks, not just synthetic API latency.

If checkout slows down under load, inspect:

  • Session driver performance
  • Database writes during cart updates
  • Coupon and inventory queries
  • Synchronous event listeners
  • Payment provider timeout handling

Scenario 3: File Upload and Media Processing in Laravel

Laravel apps frequently support uploads for avatars, documents, product images, or support attachments. Upload endpoints are expensive because they involve request parsing, validation, filesystem or S3 writes, and often image processing jobs.

python
from locust import HttpUser, task, between
 
class LaravelUploadUser(HttpUser):
    wait_time = between(3, 6)
    token = None
 
    def on_start(self):
        response = self.client.post(
            "/api/login",
            json={
                "email": "media.user@example.com",
                "password": "Testing123!"
            },
            headers={"Accept": "application/json"},
            name="POST /api/login"
        )
        if response.status_code == 200:
            self.token = response.json().get("token")
 
    def auth_headers(self):
        return {
            "Authorization": f"Bearer {self.token}",
            "Accept": "application/json"
        }
 
    @task(2)
    def upload_avatar(self):
        if not self.token:
            return
 
        files = {
            "avatar": ("avatar.jpg", open("testdata/avatar.jpg", "rb"), "image/jpeg")
        }
 
        self.client.post(
            "/api/user/avatar",
            files=files,
            headers=self.auth_headers(),
            name="POST /api/user/avatar"
        )
 
    @task(1)
    def upload_invoice_pdf(self):
        if not self.token:
            return
 
        files = {
            "document": ("invoice.pdf", open("testdata/invoice.pdf", "rb"), "application/pdf")
        }
 
        data = {
            "type": "invoice",
            "reference_id": "ORD-50128"
        }
 
        self.client.post(
            "/api/documents",
            files=files,
            data=data,
            headers=self.auth_headers(),
            name="POST /api/documents"
        )

What this tests

This scenario is useful for Laravel stress testing when your app handles media or document workflows. It helps measure:

  • Upload endpoint throughput
  • Validation overhead
  • Temporary storage performance
  • S3 or local disk write speed
  • Queue dispatch behavior for thumbnails or OCR jobs
  • API timeout resilience under larger payloads

For realistic results, use representative file sizes and monitor both app servers and storage backends.

Analyzing Your Results

After running your Laravel load test in LoadForge, focus on more than just average response time. Good performance testing looks at the full behavior of the system under sustained and peak traffic.

Key metrics to review

Response Time Percentiles

Averages can hide serious problems. Look at:

  • 50th percentile for typical user experience
  • 95th percentile for degraded performance
  • 99th percentile for worst-case latency

If your Laravel homepage averages 200 ms but the 95th percentile is 2.5 seconds, some users are already having a poor experience.

Requests Per Second

This tells you how much traffic your Laravel app can sustain. Watch whether throughput increases linearly as users ramp up or plateaus early due to worker, database, or cache limits.

Error Rate

Pay close attention to:

  • 500 errors from unhandled exceptions
  • 502/504 gateway errors from upstream timeouts
  • 419 errors from CSRF/session issues
  • 429 responses from throttling middleware
  • Authentication failures due to token or session problems

Endpoint-Level Performance

Break down results by route name. In Laravel, a single slow endpoint like /api/orders or /checkout can dominate overall user experience.

Infrastructure Correlation

Compare LoadForge results with server metrics such as:

  • PHP-FPM active/max children
  • CPU and memory usage
  • MySQL slow queries and connection count
  • Redis latency
  • Queue depth in Horizon
  • Disk I/O for upload-heavy scenarios

LoadForge’s real-time reporting helps you spot these trends during the test, and distributed testing lets you validate whether latency changes by geography.

Questions to ask after each test

  • Which Laravel endpoints degrade first?
  • Does latency rise gradually or suddenly?
  • Is the bottleneck in PHP, database, cache, or external services?
  • Do authenticated users perform much worse than anonymous users?
  • Are background jobs keeping up with request volume?
  • Does performance recover quickly after peak load?

Performance Optimization Tips

Once your Laravel load testing uncovers bottlenecks, use these optimization strategies.

Optimize Database Access

  • Add indexes for frequently filtered and sorted columns
  • Use eager loading to eliminate N+1 queries
  • Avoid loading large datasets into memory
  • Cache expensive query results where appropriate
  • Review pagination strategy on large tables

Cache Aggressively but Carefully

  • Enable config and route caching in production
  • Use Redis for sessions and cache
  • Cache expensive dashboard summaries and aggregate queries
  • Cache rendered fragments or API responses when possible

Useful Laravel production commands include:

bash
php artisan config:cache
php artisan route:cache
php artisan view:cache

Tune PHP-FPM or Use Octane

If you are running Laravel on PHP-FPM, make sure worker counts, memory limits, and process management settings match expected concurrency. For high-throughput applications, Laravel Octane may improve performance by reducing bootstrap overhead.

Move Heavy Work to Queues

Email sending, report generation, thumbnail creation, and webhook dispatches should not block user requests. Offload them to queues and ensure workers scale with traffic.

Reduce Middleware and Serialization Overhead

Audit global middleware, response transformers, and API resources. Small inefficiencies become major costs under load.

Use CDN and Edge Caching

Static assets, images, and cacheable content should not consume application resources unnecessarily.

Test from Multiple Regions

If your users are global, use LoadForge global test locations to evaluate how Laravel performs across different geographies and CDN paths.

Common Pitfalls to Avoid

Laravel load testing can produce misleading results if the test design is unrealistic. Avoid these common mistakes.

Testing Only the Homepage

A homepage test is useful, but it rarely reflects your application’s true load profile. Include login, dashboards, search, cart, checkout, and write-heavy endpoints.

Ignoring Authentication Patterns

Laravel apps often use session auth, Sanctum, or Passport. If you skip authentication workflows, you miss middleware, session storage, token creation, and user-specific query costs.

Using Unrealistic Test Data

If every virtual user hits the same order ID, product, or account, you may create artificial caching effects or lock contention. Use varied IDs and realistic datasets.

Forgetting CSRF and Session Requirements

Traditional Laravel web routes often require CSRF tokens and cookies. If you are testing form submissions on web.php routes, make sure your scripts reflect real browser behavior or use API routes designed for programmatic access.

Overlooking Background Systems

Laravel apps often rely on Redis, queues, email providers, object storage, and third-party APIs. A fast HTTP response does not always mean the system is healthy.

Running Tests Against Production Without Controls

Stress testing production can impact real users. Prefer a staging environment or carefully controlled off-peak tests.

Not Automating Performance Testing

A single one-off test is not enough. Integrate load testing into release workflows. LoadForge supports CI/CD integration, making it easier to catch Laravel performance regressions before deployment.

Conclusion

Laravel makes it easy to build feature-rich applications, but scalability does not happen automatically. With proper load testing, performance testing, and stress testing, you can identify slow endpoints, validate authentication flows, measure checkout performance, and ensure your Laravel app is ready for real user traffic.

Using realistic Locust scripts in LoadForge, you can simulate everything from anonymous browsing to authenticated API usage, shopping cart workflows, and file uploads. Combined with LoadForge’s distributed testing, cloud-based infrastructure, real-time reporting, and global test locations, you get a practical way to test Laravel at meaningful scale.

If you want to uncover bottlenecks before your users do, now is the perfect time to start. Try LoadForge and build a Laravel load testing workflow that gives your team confidence in every release.

Try LoadForge free for 7 days

Set up your first load test in under 2 minutes. No commitment.