> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate Limiting anpassen

> Ein tiefer Blick auf die Facade RateLimiter — von der Implementierung individueller Rate Limits pro Nutzer, Plan oder IP-Adresse bis hin zu hochverfügbaren Konfigurationen mit Redis.

## Wie die Facade `RateLimiter` funktioniert

Das Rate Limiting in Laravel setzt sich aus der Klasse `Illuminate\Cache\RateLimiting\Limit` und der Facade `RateLimiter` zusammen. Intern speichert es Zähler im Cache-Treiber (standardmäßig File oder Redis) und verfolgt so die Anzahl der Anfragen.

Die Middleware `throttle` führt für jede Anfrage die per `RateLimiter::for()` definierte Closure aus und liefert bei Überschreitung `429 Too Many Requests`.

## Definition eigener Limiter im `AppServiceProvider`

Die Rate-Limit-Konfiguration erfolgt in der `boot()`-Methode von `App\Providers\AppServiceProvider`.

```php theme={null}
<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
}
```

Das erste Argument von `RateLimiter::for()` ist der Limiter-Name, den die Middleware `throttle` verwendet. Die Closure als zweites Argument muss eine Instanz von `Illuminate\Cache\RateLimiting\Limit` zurückgeben.

## Rate Limits nach Nutzer, IP-Adresse oder Plan

### Unterschiedliche Limits für authentifizierte Nutzer und Gäste

```php theme={null}
RateLimiter::for('uploads', function (Request $request) {
    return $request->user()
        ? Limit::perHour(100)->by($request->user()->id)
        : Limit::perHour(10)->by($request->ip());
});
```

### Limits nach Nutzerplan

```php theme={null}
RateLimiter::for('api', function (Request $request) {
    $user = $request->user();

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

    return match ($user->plan) {
        'enterprise' => Limit::none(),
        'pro'        => Limit::perMinute(500)->by($user->id),
        default      => Limit::perMinute(60)->by($user->id),
    };
});
```

### Globales Limit je IP-Adresse

Throttling pro IP-Adresse, unabhängig vom Endpunkt.

```php theme={null}
RateLimiter::for('global', function (Request $request) {
    return Limit::perMinute(1000)->by($request->ip());
});
```

### Mehrere Limits kombinieren

Ein Array führt dazu, dass alle Limits ausgewertet werden. Sobald eines erreicht wird, folgt `429`.

```php theme={null}
RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(10)->by($request->ip()),
        Limit::perMinute(5)->by($request->input('email')),
    ];
});
```

<Info>
  Wenn Sie mehrere Limits mit demselben `by`-Wert definieren, versehen Sie die Keys mit Präfixen, damit sie nicht kollidieren.
</Info>

```php theme={null}
RateLimiter::for('uploads', function (Request $request) {
    return [
        Limit::perMinute(10)->by('minute:' . $request->user()->id),
        Limit::perDay(1000)->by('day:' . $request->user()->id),
    ];
});
```

## Angabe eigener Limiter-Namen an der `throttle`-Middleware

Übergeben Sie den definierten Limiter-Namen an die Middleware `throttle`.

```php theme={null}
use Illuminate\Support\Facades\Route;

Route::middleware(['throttle:api'])->group(function () {
    Route::get('/user', function () { /* ... */ });
    Route::post('/posts', function () { /* ... */ });
});
```

### Registrierung in `bootstrap/app.php`

Ab Laravel 11 verwalten Sie Middleware in `bootstrap/app.php`.

```php theme={null}
use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        apiPrefix: 'api',
    )
    ->withMiddleware(function (\Illuminate\Foundation\Configuration\Middleware $middleware): void {
        $middleware->throttleApi('api');
    })
    ->create();
```

## Beispielhafte Anwendung auf API-Routen

<Steps>
  <Step title="Limiter definieren">
    Definieren Sie im `AppServiceProvider` mehrere Limiter.

    ```php theme={null}
    public function boot(): void
    {
        // Allgemeiner API-Zugriff
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        // Datei-Uploads
        RateLimiter::for('uploads', function (Request $request) {
            return $request->user()?->isPro()
                ? Limit::perHour(500)->by($request->user()->id)
                : Limit::perHour(50)->by($request->user()?->id ?: $request->ip());
        });

        // Login-Versuche
        RateLimiter::for('login', function (Request $request) {
            return [
                Limit::perMinute(10)->by($request->ip()),
                Limit::perMinute(5)->by($request->input('email')),
            ];
        });
    }
    ```
  </Step>

  <Step title="Middleware an Routen anwenden">
    ```php theme={null}
    // routes/api.php
    use Illuminate\Support\Facades\Route;

    Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
        Route::get('/user', [\App\Http\Controllers\UserController::class, 'show']);
        Route::get('/posts', [\App\Http\Controllers\PostController::class, 'index']);
    });

    Route::middleware(['auth:sanctum', 'throttle:uploads'])->group(function () {
        Route::post('/uploads', [\App\Http\Controllers\UploadController::class, 'store']);
    });

    Route::middleware(['throttle:login'])->group(function () {
        Route::post('/login', [\App\Http\Controllers\AuthController::class, 'login']);
    });
    ```
  </Step>
</Steps>

## Response-Header (`X-RateLimit-*`)

Die Middleware `throttle` fügt Limit-Infos automatisch als Response-Header an.

| Header                  | Beschreibung                                              |
| ----------------------- | --------------------------------------------------------- |
| `X-RateLimit-Limit`     | Erlaubte Anfragezahl                                      |
| `X-RateLimit-Remaining` | Verbleibende Anfragen                                     |
| `Retry-After`           | Sekunden bis zur nächsten möglichen Anfrage (nur bei 429) |
| `X-RateLimit-Reset`     | UNIX-Zeitstempel, an dem das Limit zurückgesetzt wird     |

```http theme={null}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 45
X-RateLimit-Reset: 1717000000
Content-Type: application/json

{
    "message": "Too Many Requests."
}
```

### Eigene Response zurückgeben

```php theme={null}
RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)
        ->by($request->user()?->id ?: $request->ip())
        ->response(function (Request $request, array $headers) {
            return response()->json([
                'message' => 'Anfragelimit überschritten. Bitte versuchen Sie es später erneut.',
                'retry_after' => $headers['Retry-After'],
            ], 429, $headers);
        });
});
```

## Manuelle Prüfung mit `RateLimiter::attempt()`

Wenn Sie das Rate Limit unabhängig von der `throttle`-Middleware zu einem beliebigen Zeitpunkt im Code prüfen wollen, nutzen Sie `RateLimiter::attempt()`.

```php theme={null}
use Illuminate\Support\Facades\RateLimiter;

class SmsController extends Controller
{
    public function send(Request $request): \Illuminate\Http\JsonResponse
    {
        $key = 'sms:' . $request->user()->id;

        $executed = RateLimiter::attempt(
            key: $key,
            maxAttempts: 5,
            callback: function () use ($request) {
                app(SmsService::class)->send(
                    $request->user()->phone,
                    $request->input('message')
                );
            },
            decaySeconds: 3600,  // 1 Stunde
        );

        if (! $executed) {
            $seconds = RateLimiter::availableIn($key);

            return response()->json([
                'message' => "SMS-Sendelimit überschritten. Bitte in {$seconds} Sekunden erneut versuchen.",
            ], 429);
        }

        return response()->json(['message' => 'SMS gesendet.']);
    }
}
```

### Anzahl der Versuche prüfen und zurücksetzen

```php theme={null}
// Aktuelle Anzahl an Versuchen
$hits = RateLimiter::attempts($key);

// Sekunden bis zum nächsten Reset
$seconds = RateLimiter::availableIn($key);

// Ist das Limit erreicht?
$tooMany = RateLimiter::tooManyAttempts($key, $maxAttempts = 5);

// Zähler manuell zurücksetzen (z. B. nach Logout)
RateLimiter::clear($key);
```

### Beispiel: Login-Throttling

```php theme={null}
public function login(Request $request): mixed
{
    $key = 'login:' . $request->input('email');

    if (RateLimiter::tooManyAttempts($key, 5)) {
        $seconds = RateLimiter::availableIn($key);

        throw ValidationException::withMessages([
            'email' => "Zu viele Login-Versuche. Bitte in {$seconds} Sekunden erneut versuchen.",
        ]);
    }

    if (! Auth::attempt($request->only('email', 'password'))) {
        RateLimiter::hit($key, 300);  // 5 Minuten zählen

        throw ValidationException::withMessages([
            'email' => 'E-Mail-Adresse oder Passwort falsch.',
        ]);
    }

    RateLimiter::clear($key);

    return redirect()->intended('/dashboard');
}
```

## Response-basiertes Rate Limiting

Wenn nur bestimmte Responses gezählt werden sollen, verwenden Sie `after()`. Ein Beispiel, das nur 404-Responses zählt, um Enumeration-Angriffe zu verhindern:

```php theme={null}
use Symfony\Component\HttpFoundation\Response;

RateLimiter::for('resource-lookup', function (Request $request) {
    return Limit::perMinute(10)
        ->by($request->user()?->id ?: $request->ip())
        ->after(function (Response $response) {
            return $response->getStatusCode() === 404;
        });
});
```

## Rate Limiting mit Redis

Wenn Sie den Cache-Standardtreiber auf Redis umstellen, nutzt auch die `throttle`-Middleware automatisch Redis.

### Redis-Treiber konfigurieren

```php theme={null}
// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),
```

```ini theme={null}
# .env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
```

### `throttleWithRedis` verwenden

Für die Redis-optimierte Throttle-Middleware rufen Sie in `bootstrap/app.php` die Methode `throttleWithRedis()` auf.

```php theme={null}
use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        apiPrefix: 'api',
    )
    ->withMiddleware(function (\Illuminate\Foundation\Configuration\Middleware $middleware): void {
        $middleware->throttleWithRedis();
    })
    ->create();
```

Dadurch wird die `throttle`-Middleware auf die Klasse `ThrottleRequestsWithRedis` gemappt und nutzt atomare Redis-Operationen für präzises Zählen.

<Warning>
  Wenn Sie `throttleWithRedis()` verwenden, stellen Sie sicher, dass Redis stets verfügbar ist. Fällt die Verbindung zu Redis aus, können unter Umständen alle Anfragen abgewiesen werden.
</Warning>

### Vorteile von Redis

* **Horizontal skalierbar** — Zähler werden über mehrere Serverinstanzen geteilt.
* **Hohe Genauigkeit** — atomare Operationen verhindern Race Conditions.
* **TTL-Verwaltung** — die native Ablaufzeit von Redis löscht die Zähler automatisch.

## Verwandte Seiten

<Card title="Cache" icon="database" href="/de/cache">
  Erfahren Sie mehr über Konfiguration und Verwendung der Laravel-Cache-Treiber inklusive Redis.
</Card>


## Related topics

- [Einen MCP-Server mit Laravel bauen](/de/advanced/mcp-server.md)
- [Laravel-Updates März 2026](/de/blog/changelog/202603.md)
- [Laravel Agent Detector – Paket zur Erkennung von KI-Agenten](/de/blog/agent-detector-introduction.md)
- [Middleware](/de/middleware.md)
- [Laravel Pennant](/de/pennant.md)
