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=s3 -
Validate 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-check -
Pin 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/php -
Run migrations on deploy
heroku run php artisan migrate --force -
Configure app for Heroku
// config/trustedproxy.php protected $proxies = '*';
Testing & Deployment
-
Run Laravel security checks
composer require enlightn/enlightn --dev php artisan enlightn -
Test on Heroku staging
heroku create myapp-staging git push staging main -
Write 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