Mastering Laravel API Rate Limiting to Secure Endpoints

1. Introduction: Why Laravel API Rate Limiting Matters

In today’s API-driven world, protecting your endpoints from abuse is not just good practice—it’s essential. Rate limiting is a crucial defense mechanism for your Laravel applications, preventing malicious attacks, reducing server load, and ensuring fair resource allocation among users.

Without proper rate limiting, your API is vulnerable to:

  • Denial of Service (DoS) attacks
  • Brute force attempts on authentication endpoints
  • Excessive resource consumption by greedy clients
  • Increased infrastructure costs due to unnecessary traffic
  • Degraded performance for legitimate users

This comprehensive guide will walk you through implementing robust rate-limiting strategies in Laravel, from basic IP-based throttling to advanced custom solutions using the Laravel Throttle package. By the end, you’ll have the knowledge to deploy a secure, optimized API that can handle real-world traffic patterns while protecting against common threats.

2. What Is API Rate Limiting?

Rate limiting is a strategy for controlling the amount of incoming and outgoing traffic to or from a network, API, or service. In the context of web applications, it restricts the number of requests a client can make to your API within a specific time frame.

For example, you might configure your API to allow:

  • 60 requests per minute per IP address
  • 1000 requests per day per API key
  • 5 login attempts per hour for a single user

When a client exceeds these limits, the server typically responds with a 429 “Too Many Requests” HTTP status code, often including headers that indicate when the client can resume making requests.

Rate limiting serves multiple purposes:

  • Security: Prevents brute force attacks and DDoS attempts
  • Resource management: Ensures fair distribution of server resources
  • Cost control: Limits API usage based on subscription tiers
  • Performance optimization: Prevents server overload during traffic spikes

3. Common Rate Limiting Strategies

IP Address

The most basic form of rate limiting tracks requests by the client’s IP address. This approach is simple to implement but has limitations:

Pros:

  • Easy to implement
  • Requires no client configuration
  • Works for all types of requests

Cons:

  • Multiple users behind a NAT or proxy may share the same IP
  • IP addresses can be spoofed
  • Mobile users may frequently change IPs

API Key

API keys provide a more reliable way to identify and throttle clients:

Pros:

  • More precise client identification
  • Can be revoked individually
  • Allows for different rate limits per client

Cons:

  • Requires key management infrastructure
  • Keys can be stolen if not properly secured
  • Additional overhead for authentication

Client ID

Using unique client identifiers can provide fine-grained control:

Pros:

  • Can be tied directly to user accounts
  • Enables tiered access levels
  • More difficult to circumvent than IP-based limits

Cons:

  • Requires user registration/authentication
  • More complex to implement
  • Increased database load for tracking

4. Understanding Laravel Middleware

Before diving into rate-limiting implementation, it’s important to understand Laravel’s middleware architecture. Middleware acts as a filtering mechanism for HTTP requests entering your application.

Laravel’s middleware provides a convenient layer where rate limiting logic can be applied:

namespace App\Http\Middleware;

use Closure;

class RateLimitMiddleware
{
    public function handle($request, Closure $next)
    {
        // Rate limiting logic goes here

        return $next($request);
    }
}

Laravel comes with built-in rate limiting middleware, but understanding how middleware works will help you implement custom solutions when needed.

Key middleware concepts for rate limiting:

  • Middleware can be applied globally, to route groups, or individual routes
  • Order matters when applying multiple middleware
  • Middleware can terminate early, preventing request processing
  • Response headers can be modified to provide rate limit information

5. Setting Up the Laravel API Project

Prerequisites

Before starting, ensure you have:

  • PHP 8.0 or higher
  • Composer
  • Git
  • A database system (MySQL, PostgreSQL, etc.)
  • Basic knowledge of Laravel

Cloning the Project

Let’s start by cloning a basic Laravel API project:

git clone https://github.com/yourusername/laravel-api-rate-limiting.git
cd laravel-api-rate-limiting

If you don’t have an existing project, create a new Laravel project:

composer create-project laravel/laravel laravel-api-rate-limiting
cd laravel-api-rate-limiting

Installing Dependencies & Serving the App

Install the required dependencies:

composer install
cp .env.example .env
php artisan key:generate

Configure your database connection in the .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_api
DB_USERNAME=root
DB_PASSWORD=

Run migrations to set up your database:

php artisan migrate

Start the development server:

php artisan serve

6. Remote Database Configuration in Hostinger

If you’re using Hostinger as your hosting provider, configuring your database is straightforward:

  1. Log in to your Hostinger dashboard
  2. Navigate to Databases and click “Add Database”
  3. Select MySQL as the database type
  4. Choose your preferred region
  5. Name your database (e.g., laravel_api)
  6. Create a new user or select an existing one
  7. Note the connection details provided

Update your .env file with the Hostinger database credentials:

DB_CONNECTION=mysql
DB_HOST=your-Hostinger-db-host
DB_PORT=3306
DB_DATABASE=laravel_api
DB_USERNAME=your-username
DB_PASSWORD=your-password

To ensure secure connections, Hostinger requires SSL for database connections. Add the following to your config/database.php file:

'mysql' => [
    // other configuration...
    'sslmode' => env('DB_SSLMODE', 'require'),
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
    ]) : [],
],

7. Exploring Available API Routes

Let’s create some example API routes that we’ll protect with rate limiting:

// routes/api.php

use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\API\ProductController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// Public routes
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
Route::get('/products', [ProductController::class, 'index']);

// Protected routes
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
    Route::apiResource('/products/manage', ProductController::class)->except(['index']);
});

Create the necessary controllers:

php artisan make:controller API/AuthController
php artisan make:controller API/ProductController --resource

Implement basic controller logic for testing:

// App/Http/Controllers/API/AuthController.php
namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'user' => $user,
            'access_token' => $token,
            'token_type' => 'Bearer',
        ]);
    }

    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);

        if (!Auth::attempt($request->only('email', 'password'))) {
            throw ValidationException::withMessages([
                'email' => ['The provided credentials are incorrect.'],
            ]);
        }

        $user = User::where('email', $request->email)->firstOrFail();
        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'user' => $user,
            'access_token' => $token,
            'token_type' => 'Bearer',
        ]);
    }
}

8. Installing Laravel Throttle Package

While Laravel includes built-in rate limiting capabilities, we’ll enhance our implementation with the laravel throttle package for more advanced features:

composer require graham-campbell/throttle

Publish the configuration file:

php artisan vendor:publish --provider="GrahamCampbell\Throttle\ThrottleServiceProvider"

This creates a config/throttle.php file that you can customize:

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Cache Driver
    |--------------------------------------------------------------------------
    |
    | This defines the cache driver to be used. It may be the name of any
    | driver set in config/cache.php. Setting it to null will use the driver
    | you have set as default in config/cache.php.
    |
    | Default: null
    |
    */
    'driver' => null,
];

9. Rate Limiting Techniques in Laravel

9.1 Blocking Specific IP Addresses

Before implementing rate limiting, you might want to completely block certain IP addresses known for abuse:

Create a middleware to block specific IPs:

php artisan make:middleware BlockIpAddresses

Implement the middleware:

// app/Http/Middleware/BlockIpAddresses.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class BlockIpAddresses
{
    public $blockedIps = [
        '192.168.1.1',
        '10.0.0.5',
        // Add more IPs as needed
    ];

    public function handle(Request $request, Closure $next)
    {
        if (in_array($request->ip(), $this->blockedIps)) {
            abort(403, 'You are restricted to access the site.');
        }

        return $next($request);
    }
}

Register the middleware in app/Http/Kernel.php:

protected $routeMiddleware = [
    // Other middleware...
    'blockip' => \App\Http\Middleware\BlockIpAddresses::class,
];

Apply it to routes:

// routes/api.php
Route::middleware(['blockip'])->group(function () {
    // Your routes here
});

9.2 Throttling by IP Address

Laravel provides a built-in throttle middleware for IP-based rate limiting:

// routes/api.php
// Allow 60 requests per minute per IP
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
});

For more granular control, create a custom middleware:

php artisan make:middleware IpThrottleMiddleware

Implement IP-based throttling:

// app/Http/Middleware/IpThrottleMiddleware.php
namespace App\Http\Middleware;

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

class IpThrottleMiddleware
{
    protected $limiter;

    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    public function handle(Request $request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $key = 'ip:' . $request->ip();

        if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
            $seconds = $this->limiter->availableIn($key);

            return response()->json([
                'message' => 'Too many requests. Please try again later.',
                'retry_after' => $seconds,
            ], Response::HTTP_TOO_MANY_REQUESTS)
            ->header('Retry-After', $seconds);
        }

        $this->limiter->hit($key, $decayMinutes * 60);

        $response = $next($request);

        // Add rate limit headers to the response
        $response->headers->add([
            'X-RateLimit-Limit' => $maxAttempts,
            'X-RateLimit-Remaining' => $maxAttempts - $this->limiter->attempts($key) + 1,
        ]);

        return $response;
    }
}

Register and use the middleware:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // Other middleware...
    'ip.throttle' => \App\Http\Middleware\IpThrottleMiddleware::class,
];

// routes/api.php
Route::middleware('ip.throttle:30,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
});

9.3 Throttling by User ID and Session

For authenticated routes, throttling by user ID provides more accurate control:

// routes/api.php
Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});

Create a custom middleware for user-based throttling:

php artisan make:middleware UserThrottleMiddleware

Implement the middleware:

// app/Http/Middleware/UserThrottleMiddleware.php
namespace App\Http\Middleware;

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

class UserThrottleMiddleware
{
    protected $limiter;

    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    public function handle(Request $request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        // Use user ID if authenticated, otherwise use IP
        $key = $request->user() 
            ? 'user:' . $request->user()->id 
            : 'ip:' . $request->ip();

        if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
            $seconds = $this->limiter->availableIn($key);

            return response()->json([
                'message' => 'Too many requests. Please try again later.',
                'retry_after' => $seconds,
            ], Response::HTTP_TOO_MANY_REQUESTS)
            ->header('Retry-After', $seconds);
        }

        $this->limiter->hit($key, $decayMinutes * 60);

        $response = $next($request);

        $response->headers->add([
            'X-RateLimit-Limit' => $maxAttempts,
            'X-RateLimit-Remaining' => $maxAttempts - $this->limiter->attempts($key) + 1,
        ]);

        return $response;
    }
}

Register and use the middleware:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // Other middleware...
    'user.throttle' => \App\Http\Middleware\UserThrottleMiddleware::class,
];

// routes/api.php
Route::middleware('user.throttle:120,1')->group(function () {
    Route::post('/login', [AuthController::class, 'login']);
    Route::post('/register', [AuthController::class, 'register']);
});

9.4 Throttling Using Custom Headers (e.g., API Keys)

For API-key based throttling, create a middleware that looks for an API key in the request headers:

php artisan make:middleware ApiKeyThrottleMiddleware

Implement the middleware:

// app/Http/Middleware/ApiKeyThrottleMiddleware.php
namespace App\Http\Middleware;

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

class ApiKeyThrottleMiddleware
{
    protected $limiter;

    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    public function handle(Request $request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        // Get API key from header or fallback to IP
        $apiKey = $request->header('X-API-KEY');
        $key = $apiKey ? 'api:' . $apiKey : 'ip:' . $request->ip();

        if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
            $seconds = $this->limiter->availableIn($key);

            return response()->json([
                'message' => 'API rate limit exceeded. Please try again later.',
                'retry_after' => $seconds,
            ], Response::HTTP_TOO_MANY_REQUESTS)
            ->header('Retry-After', $seconds);
        }

        $this->limiter->hit($key, $decayMinutes * 60);

        $response = $next($request);

        $response->headers->add([
            'X-RateLimit-Limit' => $maxAttempts,
            'X-RateLimit-Remaining' => $maxAttempts - $this->limiter->attempts($key) + 1,
        ]);

        return $response;
    }
}

Register and use the middleware:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // Other middleware...
    'apikey.throttle' => \App\Http\Middleware\ApiKeyThrottleMiddleware::class,
];

// routes/api.php
Route::middleware('apikey.throttle:100,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
});

9.5 Tiered Rate Limits for Guests vs. Authenticated Users

Different user types often deserve different rate limits. Let’s implement a tiered approach:

php artisan make:middleware TieredRateLimitMiddleware

Implement the middleware:

// app/Http/Middleware/TieredRateLimitMiddleware.php
namespace App\Http\Middleware;

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

class TieredRateLimitMiddleware
{
    protected $limiter;

    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    public function handle(Request $request, Closure $next)
    {
        // Define different limits based on authentication status
        if ($request->user()) {
            // Authenticated user - higher limits
            $maxAttempts = 120;
            $decayMinutes = 1;
            $key = 'user:' . $request->user()->id;

            // Premium users can get even higher limits
            if ($request->user()->isPremium()) {
                $maxAttempts = 300;
            }
        } else {
            // Guest user - lower limits
            $maxAttempts = 30;
            $decayMinutes = 1;
            $key = 'ip:' . $request->ip();
        }

        if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
            $seconds = $this->limiter->availableIn($key);

            return response()->json([
                'message' => 'Rate limit exceeded. Please try again later.',
                'retry_after' => $seconds,
            ], Response::HTTP_TOO_MANY_REQUESTS)
            ->header('Retry-After', $seconds);
        }

        $this->limiter->hit($key, $decayMinutes * 60);

        $response = $next($request);

        $response->headers->add([
            'X-RateLimit-Limit' => $maxAttempts,
            'X-RateLimit-Remaining' => $maxAttempts - $this->limiter->attempts($key) + 1,
        ]);

        return $response;
    }
}

Add the isPremium method to the User model:

// app/Models/User.php
public function isPremium()
{
    // Logic to determine if user has premium status
    return $this->subscription_type === 'premium';
}

Register and use the middleware:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // Other middleware...
    'tiered.throttle' => \App\Http\Middleware\TieredRateLimitMiddleware::class,
];

// routes/api.php
Route::middleware('tiered.throttle')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    // Other routes that should have tiered rate limiting
});

10. Advanced Throttling with Laravel Throttle Package

The Laravel Throttle package provides more advanced features for rate limiting. Let’s explore how to use its key methods:

First, inject the throttle manager into your controller:

// app/Http/Controllers/API/AdvancedThrottleController.php
namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use GrahamCampbell\Throttle\Facades\Throttle;
use Illuminate\Http\Request;

class AdvancedThrottleController extends Controller
{
    public function throttledEndpoint(Request $request)
    {
        // Implementation will go here
    }
}

attempt

The attempt method checks if an action can be performed and increments the counter if allowed:

public function throttledEndpoint(Request $request)
{
    $key = $request->user() ? $request->user()->id : $request->ip();

    // Allow 5 attempts per minute
    $limit = 5;
    $time = 60;

    if (Throttle::attempt(['key' => $key, 'limit' => $limit, 'time' => $time])) {
        // Action is allowed
        return response()->json(['message' => 'Request processed successfully']);
    } else {
        // Too many attempts
        return response()->json(['message' => 'Too many attempts, please try again later'], 429);
    }
}

hit

The hit method increments the counter without checking the limit:

public function incrementCounter(Request $request)
{
    $key = $request->ip();

    // Increment the counter with 60 seconds expiry
    Throttle::hit(['key' => $key, 'time' => 60]);

    return response()->json(['message' => 'Counter incremented']);
}

clear

The clear method resets the counter:

public function resetCounter(Request $request)
{
    $key = $request->ip();

    Throttle::clear(['key' => $key]);

    return response()->json(['message' => 'Counter reset successfully']);
}

count

The count method returns the current counter value:

public function getCounter(Request $request)
{
    $key = $request->ip();

    $count = Throttle::count(['key' => $key]);

    return response()->json(['counter' => $count]);
}

check

The check method verifies if an action is allowed without incrementing the counter:

public function checkLimit(Request $request)
{
    $key = $request->ip();
    $limit = 5;
    $time = 60;

    $allowed = Throttle::check(['key' => $key, 'limit' => $limit, 'time' => $time]);

    return response()->json([
        'allowed' => $allowed,
        'remaining_attempts' => $allowed ? ($limit - Throttle::count(['key' => $key])) : 0
    ]);
}

Create a route for the advanced throttle controller:

// routes/api.php
Route::get('/advanced-throttle', [AdvancedThrottleController::class, 'throttledEndpoint']);
Route::get('/advanced-throttle/count', [AdvancedThrottleController::class, 'getCounter']);
Route::get('/advanced-throttle/check', [AdvancedThrottleController::class, 'checkLimit']);
Route::post('/advanced-throttle/reset', [AdvancedThrottleController::class, 'resetCounter']);

11. Customizing Throttle Responses

Customize how your API responds when rate limits are exceeded by creating a custom exception handler:

// app/Exceptions/Handler.php
namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Throwable;

class Handler extends ExceptionHandler
{
    // Other methods...

    public function render($request, Throwable $exception)
    {
        if ($exception instanceof ThrottleRequestsException) {
            return response()->json([
                'error' => 'Rate limit exceeded',
                'message' => 'You have made too many requests. Please wait before trying again.',
                'retry_after' => $exception->getHeaders()['Retry-After'],
                'docs_url' => 'https://api.example.com/docs/rate-limits',
            ], 429);
        }

        return parent::render($request, $exception);
    }
}

12. Logging Rate Limit Violations

Track rate limit violations to identify potential abuse patterns:

Create a custom middleware for logging:

php artisan make:middleware LogRateLimitMiddleware

Implement the middleware:

// app/Http/Middleware/LogRateLimitMiddleware.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class LogRateLimitMiddleware
{
    protected $limiter;

    public function __construct(RateLimiter $limiter)
    {
        $this->limiter = $limiter;
    }

    public function handle(Request $request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $key = $request->user() ? 'user:' . $request->user()->id : 'ip:' . $request->ip();

        // Check if this request will exceed the limit
        if ($this->limiter->attempts($key) >= $maxAttempts - 1) {
            // Log the violation
            Log::warning('Rate limit exceeded', [
                'key' => $key,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'path' => $request->path(),
                'attempts' => $this->limiter->attempts($key),
                'max_attempts' => $maxAttempts,
            ]);

            // You could also notify admins or trigger alerts here
        }

        return $next($request);
    }
}

Register and use the middleware:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // Other middleware...
    'log.ratelimit' => \App\Http\Middleware\LogRateLimitMiddleware::class,
];

// routes/api.php
Route::middleware(['throttle:5,1', 'log.ratelimit:5,1'])->group(function () {
    Route::post('/login', [AuthController::class, 'login']);
});

13. Testing Rate Limiting Locally

To test your rate limiting implementation, create a simple test script:

php artisan make:test RateLimitingTest

Implement the test:

// tests/Feature/RateLimitingTest.php
namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class RateLimitingTest extends TestCase
{
    use RefreshDatabase;

    public function test_basic_rate_limiting()
    {
        // Make multiple requests to a rate-limited endpoint
        for ($i = 0; $i < 5; $i++) {
            $response = $this->get('/api/products');
            $response->assertStatus(200);
        }

        // The 6th request should be rate limited
        $response = $this->get('/api/products');
        $response->assertStatus(429);

        // Verify the response contains the correct headers
        $this->assertTrue($response->headers->has('Retry-After'));
    }

    public function test_user_based_rate_limiting()
    {
        // Create and authenticate a user
        $user = \App\Models\User::factory()->create();
        $this->actingAs($user);

        // Make requests up to the limit
        for ($i = 0; $i < 60; $i++) {
            $response = $this->get('/api/user');
            $response->assertStatus(200);
        }

        // The next request should be rate limited
        $response = $this->get('/api/user');
        $response->assertStatus(429);
    }
}

Run the tests:

php artisan test --filter=RateLimitingTest

14. Deploying to Hostinger

To deploy your Laravel API with rate limiting to Hostinger:

  1. Push your code to a Git repository (GitHub, GitLab, Bitbucket)
  2. Log in to your Hostinger dashboard
  3. Create a new application
  4. Select “Laravel” as your application type
  5. Connect your Git repository
  6. Configure deployment settings:
  • PHP version: 8.1+
  • Environment variables (copy from your .env file)
  • Database connection details
  1. Add the following to your Procfile:
web: vendor/bin/heroku-php-apache2 public/
  1. Configure environment variables in the Hostinger dashboard:
APP_ENV=production
APP_DEBUG=false
APP_KEY=your-app-key
DB_CONNECTION=mysql
DB_HOST=your-Hostinger-db-host
DB_PORT=3306
DB_DATABASE=your-db-name
DB_USERNAME=your-username
DB_PASSWORD=your-password
  1. Deploy your application

15. Post-Deployment: Handling Laravel Routes on Hostinger

After deployment, ensure your Laravel routes are properly handled:

Create a custom Nginx configuration for your Hostinger application:

# Hostinger-nginx-config.conf
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

Add the configuration to Hostinger through the Hostinger dashboard:

  • Navigate to your site
  • Go to “Tools & Settings” > “Nginx Configuration”
  • Paste the above configuration
  • Save changes

16. Final Testing With Postman and CURL

Testing with Postman

  1. Create a new Postman collection for your API
  2. Add requests for your endpoints
  3. Use the “Runner” feature to send multiple requests in succession
  4. Verify that rate limiting is working by checking for 429 responses
  5. Examine the response headers for rate limit information

Example test script for Postman:

// Test rate limit headers
pm.test("Rate limit headers are present", function() {
    pm.response.to.have.header("X-RateLimit-Limit");
    pm.response.to.have.header("X-RateLimit-Remaining");
});

// If rate limited, verify 429 status
if (pm.response.code === 429) {
    pm.test("Rate limited response has Retry-After header", function() {
        pm.response.to.have.header("Retry-After");
    });
}

Testing with CURL

Test your API endpoints with CURL commands:

# Make multiple requests to test rate limiting
for i in {1..10}; do
  curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://your-api.hostinger.app/api/products
  echo "\n--- Request $i complete ---\n"
  sleep 1
done

Check for the rate limit headers in the response:

curl -i -H "Accept: application/json" http://your-api.hostinger.app/api/products

17. Monitoring and Analytics for Rate Limiting

Understanding how your rate limits affect users is crucial. Let’s add a section on monitoring:

// app/Http/Middleware/RateLimitAnalyticsMiddleware.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class RateLimitAnalyticsMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        // Only track API requests
        if (strpos($request->path(), 'api/') === 0) {
            // Store analytics data
            DB::table('rate_limit_analytics')->insert([
                'path' => $request->path(),
                'method' => $request->method(),
                'status_code' => $response->status(),
                'ip' => $request->ip(),
                'user_id' => $request->user() ? $request->user()->id : null,
                'user_agent' => $request->userAgent(),
                'rate_limited' => $response->status() === 429,
                'created_at' => now(),
            ]);
        }

        return $response;
    }
}

Create the migration for the analytics table:

php artisan make:migration create_rate_limit_analytics_table

Define the migration:

// database/migrations/xxxx_xx_xx_create_rate_limit_analytics_table.php
public function up()
{
    Schema::create('rate_limit_analytics', function (Blueprint $table) {
        $table->id();
        $table->string('path');
        $table->string('method', 10);
        $table->integer('status_code');
        $table->string('ip');
        $table->unsignedBigInteger('user_id')->nullable();
        $table->string('user_agent')->nullable();
        $table->boolean('rate_limited');
        $table->timestamp('created_at');
    });
}

Register the middleware in app/Http/Kernel.php:

protected $middleware = [
    // Other middleware...
    \App\Http\Middleware\RateLimitAnalyticsMiddleware::class,
];

Create a dashboard to visualize the data:

// app/Http/Controllers/RateLimitDashboardController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class RateLimitDashboardController extends Controller
{
    public function index()
    {
        $stats = [
            'total_requests' => DB::table('rate_limit_analytics')->count(),
            'rate_limited_requests' => DB::table('rate_limit_analytics')->where('rate_limited', true)->count(),
            'top_rate_limited_paths' => DB::table('rate_limit_analytics')
                ->where('rate_limited', true)
                ->select('path', DB::raw('count(*) as count'))
                ->groupBy('path')
                ->orderBy('count', 'desc')
                ->limit(5)
                ->get(),
            'top_rate_limited_ips' => DB::table('rate_limit_analytics')
                ->where('rate_limited', true)
                ->select('ip', DB::raw('count(*) as count'))
                ->groupBy('ip')
                ->orderBy('count', 'desc')
                ->limit(5)
                ->get(),
        ];

        return view('rate-limit-dashboard', ['stats' => $stats]);
    }
}

18. Summary and Next Steps

In this comprehensive guide, we’ve explored various techniques for implementing rate limiting in Laravel APIs:

  1. Basic rate limiting using Laravel’s built-in middleware
  2. IP-based throttling for public endpoints
  3. User-based throttling for authenticated requests
  4. API key throttling for third-party integrations
  5. Tiered rate limiting for different user types
  6. Advanced throttling with the Laravel Throttle package
  7. Custom response handling for rate limit violations
  8. Logging and monitoring rate limit events
  9. Testing rate limiting implementation
  10. Deployment to Hostinger and post-deployment configuration

Next Steps

To further enhance your API’s rate limiting:

  1. Implement dynamic rate limits that adjust based on server load
  2. Add caching to reduce database load from frequent limit checks
  3. Set up alerts for unusual rate limit violations that might indicate attacks
  4. Create a rate limit status endpoint for clients to check their current limits
  5. Document your rate limits clearly in your API documentation
  6. Consider using Redis for distributed rate limiting in multi-server setups

By implementing these rate limiting strategies, you’ve taken a significant step toward building a more secure, stable, and fair API for all users. Rate limiting not only protects your infrastructure but also ensures that your services remain available and responsive even under heavy load or during attempted abuse.

Remember that rate limiting is just one aspect of API security. Continue to improve your authentication, authorization, input validation, and other security measures to build a truly robust API ecosystem.

Pay Writer

Buy author a coffee

Related posts

How to improve Website Performance & Speed Up website

10 Best WordPress Image Optimization Plugins in 2024

Top 5 hosting to WordPress Staging Site with plugins in 2024