Laravel API Security: 7 Hardened Practices I Use in Production
Laravel API Security: 7 Hardened Practices I Use in Production
1. Rate Limiting: Stop Brute Force Before It Starts
Client story: A SaaS login API was hit with 10K requests in 5 minutes. Laravel’s built-in throttle saved the server.
// routes/api.php
Route::middleware('throttle:60,1')->group(function () {
Route::post('/login', [AuthController::class, 'login']);
Route::post('/password/reset', [PasswordController::class, 'send']);
});
- 60 requests per minute per IP
- Customize per endpoint:
throttle:10,1for password reset - For API keys: use custom limiter
// App\Providers\RateLimiterServiceProvider.php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(100)->by($request->header('X-API-KEY') ?? $request->ip());
});
2. Sanctum vs JWT: Choose Once, Sleep Well
| Use Case | Winner | Why |
|---|---|---|
| SPA + Mobile | Sanctum | Cookie-based, CSRF-safe |
| Third-party apps | JWT (Tymon) | Stateless, scalable |
My Rule: Use Sanctum unless you need stateless tokens.
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
// routes/api.php
Route::middleware('auth:sanctum')->get('/user', function () {
return auth()->user();
});
Pro tip: Rotate tokens on password change
auth()->user()->tokens()->delete();
3. Bulletproof Validation with Form Requests
Never trust request()->all(). Use Form Requests.
php artisan make:request StoreUserRequest
class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'email' => 'required|email|unique:users,email',
'password' => 'required|min:8|confirmed',
'role' => 'required|in:admin,editor,user',
'ip' => 'ip'
];
}
protected function prepareForValidation()
{
$this->merge([
'ip' => $this->ip(),
'user_agent' => $this->header('User-Agent')
]);
}
}
Controller stays clean:
public function store(StoreUserRequest $request)
{
return User::create($request->validated());
}
4. API Keys + IP Whitelisting (B2B Gold)
For internal tools or partners:
// app/Http/Middleware/CheckApiKey.php
public function handle($request, Closure $next)
{
$key = $request->header('X-API-KEY');
$client = Client::where('api_key', $key)->first();
if (!$client || !in_array($request->ip(), $client->allowed_ips)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$request->merge(['client' => $client]);
return $next($request);
}
// routes/api.php
Route::middleware('api.key')->group(function () {
Route::get('/reports', [ReportController::class, 'index']);
});
5. Real-Time Monitoring with Laravel Telescope
Catch attacks as they happen.
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
Watch in /telescope:
- Failed login attempts
- Slow queries from suspicious IPs
- 429 responses (rate limit hits)
My workflow:
- Telescope → Slack alert on >50 failed logins/hour
- Auto-block IP via Cloudflare API
6. CORS & HTTPS: Non-Negotiable
// config/cors.php
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://app.yourdomain.com'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
Force HTTPS in production:
// App\Providers\AppServiceProvider.php
public function boot()
{
if (app()->environment('production')) {
URL::forceScheme('https');
}
}
Bonus: Add HSTS header via middleware:
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
7. Automated Security Scans in CI/CD
Never deploy vulnerable code.
# .github/workflows/security.yml
name: Security Audit
on: [push, pull_request]
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: composer install --no-progress
- run: ./vendor/bin/phpstan analyse
npm-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm install
- run: npm audit --audit-level=high
Add OWASP ZAP or Snyk for dependency scanning.
Final Checklist (Copy-Paste into Notion)
- [ ] Rate limiting on auth endpoints
- [ ] Sanctum + token rotation
- [ ] Form Requests for all inputs
- [ ] API key + IP whitelist (B2B)
- [ ] Telescope + Slack alerts
- [ ] CORS locked to domain
- [ ] HTTPS + HSTS enforced
- [ ] CI/CD security scans