DEV Community

Cover image for Rate Limiting and DDoS Protection for Laravel Apps on Deploynix
Deploynix
Deploynix

Posted on • Originally published at deploynix.io

Rate Limiting and DDoS Protection for Laravel Apps on Deploynix

Your Laravel application is live. Traffic is growing. Then one morning, your monitoring alerts fire. Response times spike to thirty seconds. Your server's CPU is pinned at 100%. Your database connection pool is exhausted. Is it a genuine traffic spike from a successful marketing campaign, or is someone attacking your application?

The difference between a traffic spike and a DDoS attack matters less than your preparation for both. Whether it's a bot scraping your entire site, a disgruntled user hammering your API, or a coordinated attack from a botnet, the defense strategies overlap significantly. This article covers rate limiting and DDoS protection across every layer of your stack, from your Laravel application code through Nginx, Cloudflare, and Deploynix's firewall rules.

Understanding the Threat Landscape

Before diving into solutions, it helps to understand what you're defending against.

Brute-force attacks target login forms, password reset endpoints, and API authentication. Attackers try thousands of credential combinations per minute.

Scraping and crawling involves bots systematically requesting every page on your site, consuming server resources and potentially stealing your content.

API abuse happens when a consumer (legitimate or not) sends far more requests than your API is designed to handle, whether intentionally or due to a bug in their code.

Application-layer DDoS floods your application with requests that are individually legitimate but collectively overwhelming. These are harder to detect than network-layer attacks because each request looks normal.

Network-layer DDoS (volumetric attacks) overwhelm your server's bandwidth with massive amounts of traffic. These require upstream mitigation because they saturate your network connection before traffic even reaches your application.

Layer 1: Laravel's Built-In Rate Limiter

Laravel's rate limiter is your first line of defense at the application level. It's flexible, easy to configure, and integrates naturally with your routes and middleware.

Configuring rate limiters:

Define your rate limiters in bootstrap/app.php or a service provider's boot method using the RateLimiter facade:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function ($request) {
    return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});

RateLimiter::for('login', function ($request) {
    return Limit::perMinute(5)->by($request->ip());
});

RateLimiter::for('uploads', function ($request) {
    return Limit::perMinute(10)->by($request->user()->id);
});
Enter fullscreen mode Exit fullscreen mode

Applying to routes:

Route::middleware('throttle:login')->post('/login', [AuthController::class, 'login']);

Route::middleware('throttle:api')->group(function () {
    Route::get('/users', [UserController::class, 'index']);
    Route::get('/users/{user}', [UserController::class, 'show']);
});
Enter fullscreen mode Exit fullscreen mode

Best practices for Laravel rate limiting:

  • Rate limit by authenticated user ID when possible, falling back to IP address for unauthenticated requests.
  • Use stricter limits for sensitive endpoints (login, password reset, registration).
  • Return appropriate 429 Too Many Requests responses with Retry-After headers.
  • Consider different limits for different API tiers if you have a tiered pricing model.

Dynamic rate limiting based on user plans:

RateLimiter::for('api', function ($request) {
    $user = $request->user();

    if (! $user) {
        return Limit::perMinute(10)->by($request->ip());
    }

    return match ($user->plan) {
        'enterprise' => Limit::perMinute(1000)->by($user->id),
        'professional' => Limit::perMinute(300)->by($user->id),
        'starter' => Limit::perMinute(60)->by($user->id),
        default => Limit::perMinute(30)->by($user->id),
    };
});
Enter fullscreen mode Exit fullscreen mode

Handling rate limit responses gracefully:

Laravel automatically returns a 429 response when a rate limit is exceeded. You can customize this behavior:

RateLimiter::for('api', function ($request) {
    return Limit::perMinute(60)
        ->by($request->user()?->id ?: $request->ip())
        ->response(function ($request, $headers) {
            return response()->json([
                'message' => 'Too many requests. Please slow down.',
                'retry_after' => $headers['Retry-After'],
            ], 429, $headers);
        });
});
Enter fullscreen mode Exit fullscreen mode

Layer 2: Nginx Rate Limiting

Nginx rate limiting operates before your Laravel application processes the request, which means it's faster and uses fewer resources. It's ideal for catching high-volume abuse before it reaches PHP.

Basic Nginx rate limiting:

Add a rate limit zone to your Nginx configuration:

# Define a rate limit zone (in the http block)
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

# Apply to locations (in the server block)
location / {
    limit_req zone=general burst=20 nodelay;
    # ... other directives
}

location /login {
    limit_req zone=login burst=3 nodelay;
    # ... other directives
}
Enter fullscreen mode Exit fullscreen mode

Understanding the parameters:

  • $binary_remote_addr: Uses the client's IP address as the key, stored in binary format for memory efficiency.
  • zone=general:10m: Creates a shared memory zone named "general" with 10MB of storage (holds about 160,000 IP addresses).
  • rate=10r/s: Allows 10 requests per second per IP.
  • burst=20: Allows a burst of up to 20 requests beyond the rate limit.
  • nodelay: Processes burst requests immediately rather than queuing them.

Connection limiting:

In addition to request rate limiting, you can limit the number of concurrent connections:

limit_conn_zone $binary_remote_addr zone=connlimit:10m;

server {
    limit_conn connlimit 20;
}
Enter fullscreen mode Exit fullscreen mode

This limits each IP to 20 concurrent connections, preventing a single source from monopolizing server resources.

Customizing error responses:

limit_req_status 429;
error_page 429 /429.html;
Enter fullscreen mode Exit fullscreen mode

On Deploynix servers, Nginx configuration is managed through site settings. While Deploynix provides sensible defaults, you can SSH into the server to add custom rate limiting rules to your Nginx site configuration for fine-grained control.

Layer 3: Cloudflare Protection

Cloudflare sits in front of your server and can absorb and filter traffic before it reaches your infrastructure. This is essential for defending against volumetric DDoS attacks that would overwhelm your server's bandwidth.

Setting up Cloudflare with Deploynix:

  1. Point your domain's nameservers to Cloudflare.
  2. Add your domain to Cloudflare's dashboard.
  3. Configure an A record pointing to your Deploynix server's IP.
  4. Enable Cloudflare's proxy (orange cloud icon).

Cloudflare features for protection:

  • DDoS mitigation: Cloudflare automatically detects and mitigates network-layer DDoS attacks.
  • WAF (Web Application Firewall): Blocks common attack patterns like SQL injection, XSS, and path traversal.
  • Rate limiting rules: Configure rate limits at the edge, before traffic reaches your server.
  • Bot management: Detect and challenge automated traffic.
  • Under Attack Mode: When under active attack, Cloudflare presents a JavaScript challenge to all visitors, filtering out most bot traffic.

Important Nginx configuration when using Cloudflare:

When Cloudflare proxies your traffic, all requests appear to come from Cloudflare's IP addresses. You need to configure Nginx to use the real client IP:

# Trust Cloudflare IPs
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... other Cloudflare IP ranges
real_ip_header CF-Connecting-IP;
Enter fullscreen mode Exit fullscreen mode

Without this configuration, your Nginx rate limiting will treat all visitors as a single IP address, breaking rate limiting entirely.

DNS provider integration for SSL:

Deploynix supports Cloudflare as a DNS provider for SSL certificate provisioning. If you use Cloudflare's proxy, you can use Cloudflare's SSL certificates or provision Let's Encrypt certificates through Deploynix using DNS-01 validation via Cloudflare's API. This works with Deploynix's supported DNS providers including Cloudflare, DigitalOcean, AWS Route 53, and Vultr.

Layer 4: Deploynix Firewall Rules

Deploynix's firewall management provides server-level protection that operates before any web server or application code.

Using Deploynix firewall rules for protection:

  • Block specific IP addresses or ranges that you've identified as malicious.
  • Restrict SSH access to your office or VPN IP range.
  • Block access to database ports from all external sources.
  • Allow traffic only from Cloudflare's IP ranges if you're using Cloudflare as a proxy (this prevents attackers from bypassing Cloudflare by hitting your server's IP directly).

Restricting to Cloudflare IPs:

If you use Cloudflare, configure your Deploynix firewall to only accept HTTP/HTTPS traffic from Cloudflare's IP ranges. This forces all traffic through Cloudflare's protection layer. You can manage these rules directly from the Deploynix dashboard.

Distinguishing Spikes from Attacks

Not every traffic spike is an attack. Misidentifying legitimate traffic as malicious can be worse than the attack itself, turning away real users at the moment they're most interested in your product.

Signs of legitimate traffic spikes:

  • Traffic comes from diverse geographic locations.
  • User agents are varied and include real browsers.
  • Session durations are normal (users browse multiple pages).
  • Traffic correlates with a known event (product launch, marketing campaign, social media mention).
  • Request patterns are varied (different URLs, different query parameters).

Signs of an attack:

  • Traffic comes from a small number of IP ranges.
  • Identical user agent strings across many requests.
  • Requests target a single endpoint repetitively.
  • No cookies or sessions are established.
  • Traffic patterns are perfectly uniform (exactly N requests per second, no human variation).
  • Requests come at all hours with no geographic pattern.

How to respond:

For legitimate spikes, scale your infrastructure. If you're on Deploynix, consider provisioning additional app servers behind a load balancer. Deploynix supports round robin, least connections, and IP hash load balancing methods to distribute traffic across multiple servers.

For attacks, engage your defensive layers in order: Cloudflare's Under Attack Mode, Nginx rate limiting, application-level throttling, and firewall blocks for identified attacker IPs.

Building a Defense-in-Depth Strategy

The most effective protection uses multiple layers, each catching what the previous one missed.

Layer 1 (Edge): Cloudflare

  • Absorbs volumetric attacks.
  • Filters known malicious IPs and bot signatures.
  • Applies edge rate limiting for obvious abuse patterns.

Layer 2 (Firewall): Deploynix/UFW

  • Blocks traffic that bypasses Cloudflare.
  • Restricts access to non-web ports.
  • Blocks known malicious IP ranges.

Layer 3 (Web Server): Nginx

  • Rate limits requests per IP.
  • Limits concurrent connections.
  • Returns 429 responses before PHP is invoked.

Layer 4 (Application): Laravel

  • Fine-grained rate limiting per user, per route, per plan.
  • Authentication-aware throttling.
  • Custom abuse detection logic.

Layer 5 (Monitoring): Deploynix Alerts (paid plans)

  • CPU, memory, disk, and load average monitoring.
  • Critical alerts sent via email when thresholds are exceeded.
  • Real-time visibility into server health.

Practical Configuration Checklist

Here's a practical checklist for implementing rate limiting and DDoS protection on your Deploynix-hosted Laravel application:

  1. Configure Laravel rate limiters for authentication routes (5/minute), API routes (60/minute per user), and file upload routes (10/minute per user).
  2. Add Nginx rate limiting zones for general traffic and sensitive endpoints.
  3. Set up Cloudflare with proxy enabled and Under Attack Mode ready to activate.
  4. Configure Deploynix firewall rules to restrict non-web ports.
  5. If using Cloudflare, restrict HTTP/HTTPS traffic to Cloudflare's IP ranges.
  6. Set up Deploynix health alerts to notify you of resource spikes.
  7. Test your rate limiting by sending burst requests and verifying 429 responses.
  8. Document your incident response plan so your team knows what to do during an attack.

Conclusion

Rate limiting and DDoS protection aren't optional for production Laravel applications. They're as essential as SSL certificates and database backups. The good news is that implementing effective protection doesn't require exotic tools or deep networking expertise.

Start with Laravel's built-in rate limiter for application-level control. Add Nginx rate limiting for efficient pre-application filtering. Use Cloudflare for edge protection and volumetric DDoS mitigation. Manage server-level firewall rules through Deploynix's dashboard.

The goal isn't to make your application impervious to every possible attack. It's to raise the cost of an attack high enough that attackers move on to easier targets, while ensuring legitimate users have a smooth experience even during traffic spikes. With the layered approach described in this guide and Deploynix's infrastructure management capabilities, you're well-equipped to handle both.

Top comments (0)