Laravel has robust security features built-in - configure them properly for Heroku deployment.
Expedited WAF for Laravel
Expedited WAF adds network level request filtering, preventing malicious attacks from ever touching your application. This enables true defense in depth for your Laravel app—even if your framework has a vulnerability or is improperly configured, Expedited WAF dynamically blocks threats before they reach your code.
From experience, we’ve seen many applications where attacks technically didn’t succeed, but burned through so many server resources that the application went down anyway. By filtering malicious requests at the network edge, Expedited WAF protects both your security and your availability.
New Security Features for Your Laravel App
These capabilities aren’t available out of the box with Laravel, but Expedited WAF adds them instantly:
- Block IP Addresses - Stop malicious actors by IP or CIDR range
- Block User Agents - Filter out bad bots and scrapers
- Block by Geolocation - Restrict traffic by country or region
- DDoS Protection - Stop attacks with CAPTCHA challenges
- Block Anonymous Proxies - Prevent traffic from VPNs and proxy networks
Laravel Core Security
Generate and protect APP_KEY
php artisan key:generate heroku config:set APP_KEY='base64:...'- Never commit to version control
- Rotate periodically
- Used for encryption and signed cookies
Set APP_DEBUG to false in production
heroku config:set APP_DEBUG=false heroku config:set APP_ENV=production- Never expose stack traces
- Disable detailed error messages
Configure trusted proxies
// app/Http/Middleware/TrustProxies.php protected $proxies = '*'; // Heroku uses proxy protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO;Enable HTTPS enforcement
// app/Providers/AppServiceProvider.php public function boot() { if ($this->app->environment('production')) { URL::forceScheme('https'); } }Configure session security
// config/session.php 'secure' => env('SESSION_SECURE_COOKIE', true), 'http_only' => true, 'same_site' => 'lax',
CSRF Protection
Verify CSRF middleware is enabled
// app/Http/Kernel.php - should be in $middlewareGroups['web'] \App\Http\Middleware\VerifyCsrfToken::class,Use @csrf directive in forms
<form method="POST"> @csrf <!-- form fields --> </form>Configure CSRF exceptions carefully
// app/Http/Middleware/VerifyCsrfToken.php protected $except = [ // Only add if absolutely necessary ];
Database Security
Configure Heroku Postgres
// config/database.php 'pgsql' => [ 'driver' => 'pgsql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '5432'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'prefix' => '', 'schema' => 'public', 'sslmode' => 'require', // Important for Heroku ],Use Eloquent ORM and Query Builder
- Always use parameterized queries
- Never use DB::raw() with user input
// Good User::where('email', $email)->first(); // Bad DB::select("SELECT * FROM users WHERE email = '$email'");Enable query logging carefully
// Only in development if (config('app.debug')) { DB::enableQueryLog(); }
Authentication & Authorization
Use Laravel Breeze/Jetstream
- Don’t roll your own authentication
- Use maintained authentication scaffolding
Configure password validation
// config/fortify.php or validation rules 'password' => ['required', 'string', 'min:12', 'confirmed', Rules\Password::defaults()],Implement rate limiting
// routes/web.php Route::middleware('throttle:5,1')->group(function () { Route::post('/login', [AuthController::class, 'login']); }); // app/Http/Kernel.php 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,Use Laravel Gates and Policies
// app/Policies/PostPolicy.php public function update(User $user, Post $post) { return $user->id === $post->user_id; } // In controller $this->authorize('update', $post);Protect routes with middleware
Route::middleware(['auth'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); });
File Uploads & Storage
Configure S3 for file storage
// config/filesystems.php 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), ], // .env FILESYSTEM_DRIVER=s3Validate file uploads
$request->validate([ 'avatar' => 'required|image|mimes:jpeg,png,jpg|max:2048', ]);Sanitize filenames
$filename = Str::random(40) . '.' . $file->extension();
Security Headers
Add security headers middleware
// app/Http/Middleware/SecurityHeaders.php public function handle($request, Closure $next) { $response = $next($request); $response->headers->set('X-Frame-Options', 'DENY'); $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); return $response; }Configure Content Security Policy
- Use spatie/laravel-csp package
composer require spatie/laravel-csp php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider"
Mass Assignment Protection
Use $fillable or $guarded
// app/Models/User.php protected $fillable = ['name', 'email', 'password']; // OR protected $guarded = ['id', 'is_admin'];Never use $guarded = []
Validate all input
$validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', ]);
XSS Prevention
Use Blade templating
- Blade auto-escapes by default with
{{ }} - Only use
{!! !!}when absolutely necessary
- Blade auto-escapes by default with
Sanitize HTML input
composer require mews/purifier$clean = Purifier::clean($dirty);
API Security
Use Laravel Sanctum for APIs
composer require laravel/sanctum php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"Implement API rate limiting
// routes/api.php Route::middleware('throttle:60,1')->group(function () { Route::get('/user', function (Request $request) { return $request->user(); }); });Configure CORS properly
// config/cors.php 'paths' => ['api/*'], 'allowed_origins' => [env('FRONTEND_URL')], 'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'], 'allowed_headers' => ['Content-Type', 'Authorization'],
Environment & Secrets
Store secrets in Heroku config
heroku config:set APP_KEY='...' heroku config:set DATABASE_URL='...' heroku config:set AWS_ACCESS_KEY_ID='...'Never commit .env file
- Add to .gitignore
- Use .env.example for template
Validate environment configuration
// config/app.php 'key' => env('APP_KEY'), // Will fail if not set
Logging & Monitoring
Configure production logging
// config/logging.php 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['daily'], ], ],Don’t log sensitive data
- Passwords, tokens, API keys
- Credit card information
- Personal data (GDPR)
Set up error tracking
- Use Sentry, Bugsnag, or Flare
composer require sentry/sentry-laravel
Dependency Management
Keep Laravel and packages updated
composer update composer audit # Laravel 9+Use security checker
composer require --dev enlightn/security-checker php artisan security-checkPin dependency versions
- Use composer.lock
- Review updates before deploying
Heroku Deployment
Create Procfile
web: vendor/bin/heroku-php-apache2 public/Configure buildpack
heroku buildpacks:set heroku/phpRun migrations on deploy
heroku run php artisan migrate --forceConfigure app for Heroku
// config/trustedproxy.php protected $proxies = '*';
Testing & Deployment
Run Laravel security checks
composer require enlightn/enlightn --dev php artisan enlightnTest on Heroku staging
heroku create myapp-staging git push staging mainWrite security tests
public function test_csrf_protection() { $response = $this->post('/login'); $response->assertStatus(419); // CSRF error }
Pre-Deployment Checklist
- APP_DEBUG=false
- APP_ENV=production
- All secrets in Heroku config
- SSL enforced
- Security headers configured
- File storage on S3
- Database uses SSL
- Rate limiting enabled
- CSRF protection active