Web Application Security: 10 Must-Have Best Practices

Security is not a feature you bolt on at the end, but a habit you apply from the very first line of code. The good news is that Laravel already secures many things by default — as...

Web Application Security: 10 Must-Have Best Practices

Security is not a feature you bolt on at the end, but a habit you apply from the very first line of code. The good news is that Laravel already secures many things by default — as long as you do not turn them off. This article sums up 10 must-do security practices you can apply immediately to a Laravel app, each with real code examples.

1. Turn Off Debug in Production (APP_DEBUG=false)

The most common and most dangerous mistake. When APP_DEBUG=true, every error shows a stack trace containing server paths, code snippets, and even database credentials. In the production .env file, this is mandatory:

APP_ENV=production
APP_DEBUG=false

2. Always Validate User Input

Never trust data from users. Validate every input before processing so dirty or dangerous data is rejected early:

$request->validate([
    'email'    => 'required|email|max:255',
    'age'      => 'required|integer|min:1|max:120',
    'password' => 'required|min:8|confirmed',
]);

Validation is not just about UX; it is your first line of defense against unexpected data.

3. Enable CSRF Protection

CSRF (Cross-Site Request Forgery) tricks a logged-in user into submitting a request without knowing. Laravel protects against it automatically, but every POST form must include a token:

<form method="POST" action="/profile">
    @csrf
    <input type="text" name="name">
    <button type="submit">Save</button>
</form>

Without @csrf, the request is rejected with a 419 error. Never disable the CSRF middleware just to make a form "work".

4. Prevent Mass Assignment with $fillable

Without restrictions, an attacker could smuggle a field like is_admin through a form. Declare which columns may be mass-assigned:

class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
}

Sensitive columns such as is_admin or role are deliberately left out so they cannot be changed through mass create() or update().

5. Use Query Builder / Eloquent, Avoid Raw SQL

SQL Injection happens when user input is pasted directly into a query. Eloquent and the Query Builder use prepared statements automatically. If you must use raw SQL, use parameter binding, never concatenation:

// DANGEROUS - do not do this
$users = DB::select("SELECT * FROM users WHERE email = '$email'");

// SAFE - use parameter binding
$users = DB::select('SELECT * FROM users WHERE email = ?', [$email]);

// BETTER - use Eloquent
$user = User::where('email', $email)->first();

6. Prevent XSS with Blade Escaping

XSS (Cross-Site Scripting) injects malicious scripts into a page. Blade escapes output automatically with the double curly brace syntax. The problem arises when you use {!! !!} on user data:

{{-- SAFE: escaped automatically --}}
{{ $comment->body }}

{{-- DANGEROUS if it contains user input --}}
{!! $comment->body !!}

Use {!! !!} only for HTML you fully trust, and sanitize it first with a library such as HTML Purifier when needed.

7. Force HTTPS

Without HTTPS, data including passwords is sent as plain text that can be intercepted. After installing an SSL certificate, force all URLs to use HTTPS in AppServiceProvider:

public function boot(): void
{
    if ($this->app->environment('production')) {
        URL::forceScheme('https');
    }
}

8. Apply Rate Limiting

Rate limiting caps the number of requests to prevent brute force and abuse. Apply it to sensitive routes like login or APIs:

Route::middleware('throttle:6,1')->group(function () {
    Route::post('/login', [LoginController::class, 'store']);
});

The example above limits 6 attempts per minute — enough to slow a password-guessing attack without disrupting real users.

9. Hash Passwords, Never Store Plain Text

Passwords must never be stored as-is. Laravel uses bcrypt via the Hash helper:

use Illuminate\Support\Facades\Hash;

$user->password = Hash::make($request->password);

// Verify at login
if (Hash::check($request->password, $user->password)) {
    // password matches
}

The Hash::make() function produces a one-way hash that differs each time, so a leaked database does not directly expose passwords.

10. Update Dependencies and Audit Regularly

Vulnerabilities often come from outdated third-party packages. Periodically check whether any package has a known security hole:

composer audit
composer update

Running composer audit scans against known security advisories. Apply minor updates regularly, and test with automated tests before shipping them to production.

Conclusion

  • Leverage Laravel's built-in protections — do not disable CSRF, escaping, or debug without a strong reason.
  • Treat all user input as potentially dangerous until validated.
  • Security is layered: closing one hole does not mean you are safe, apply them all.

These ten practices close the most commonly exploited holes in web apps. Making them a habit from the start is far cheaper than patching after a data breach.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Keamanan Web Security Development
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now
Tim Support
Online
Isi data dulu untuk mulai chat:
Beri rating & testimoni sebelum menutup:
Live chat by gudangcode.com