Free tool · no signup
Laravel and PHP security checklist with a score
Tick what is true for your production app and get a score out of 100 and a short list of the gaps to close first. Every item says why it matters.
By the Bug Circuit security team · Last updated
Tick what is true for your production site today. It is written for Laravel but most items apply to any PHP application. Weights reflect how often each gap is behind a real compromise.
PHP and dependencies
Configuration and secrets
Application code
Server and tooling
Backups and review
0 of 31 in place. The application is easy to compromise. Start with debug mode, updates and the web root today.
Fix these first
- PHP is a version that still receives security fixes (check php.net/supported-versions.php)
- Laravel (or your framework) is on a release that still gets security fixes
- composer audit runs in CI and a known advisory fails the build or is reviewed
- APP_DEBUG is false and APP_ENV is production on the live site
- The web server document root is the public folder, and nothing else in the project is reachable by URL
# Laravel and PHP security checklist
Score: 0% (High risk). 0 of 31 items in place.
## PHP and dependencies
- [ ] PHP is a version that still receives security fixes (check php.net/supported-versions.php)
- [ ] Laravel (or your framework) is on a release that still gets security fixes
- [ ] composer audit runs in CI and a known advisory fails the build or is reviewed
- [ ] Deploys use composer install from a committed composer.lock, never composer update on the server
- [ ] Packages marked abandoned are replaced
- [ ] Someone is alerted to new advisories for your dependencies (Dependabot, Renovate or Packagist notices)
## Configuration and secrets
- [ ] APP_DEBUG is false and APP_ENV is production on the live site
- [ ] The web server document root is the public folder, and nothing else in the project is reachable by URL
- [ ] APP_KEY is set, unique to each environment and has never been committed or shared
- [ ] The .env file is not in version control, and secrets come from the host or a secrets manager where possible
- [ ] The database user has only the rights the app needs, and the database is not open to the internet
- [ ] display_errors is off and expose_php is off in php.ini
- [ ] allow_url_include is off, and exec, system, passthru and proc_open are disabled if the app does not use them
## Application code
- [ ] Every route that reads or changes a record checks that the signed-in user owns it (policies or gates)
- [ ] Queries use Eloquent or bound parameters, and no user input is concatenated into raw SQL (whereRaw, DB::raw, selectRaw)
- [ ] Models define $fillable (or $guarded), and requests are never passed whole into create or update
- [ ] Blade output uses {{ }}, and {!! !!} appears only for content you have sanitised
- [ ] Uploads are validated by type and size, renamed and kept where PHP cannot run them
- [ ] All input goes through form requests or validators
- [ ] CSRF protection is on for all browser routes and exceptions are kept minimal
- [ ] unserialize() is never called on data a user can influence
- [ ] Login, password reset and API routes are rate limited
- [ ] Passwords are hashed with Hash::make, and admin accounts use two-factor authentication
## Server and tooling
- [ ] Telescope, Debugbar, Horizon, phpMyAdmin and similar tools are removed from production or locked behind strong authentication
- [ ] The web user can write only to storage and bootstrap/cache, not to the application code
- [ ] PHP-FPM runs as an unprivileged user, with a separate pool for each site on a shared server
- [ ] The whole site uses https, with HSTS and the usual security headers
- [ ] The web server passes only index.php to PHP, not every .php file
## Backups and review
- [ ] Database and uploaded files are backed up away from the server, and a restore has been tested
- [ ] Errors and sign-in events are logged somewhere you read, and logs hold no passwords or tokens
- [ ] A person has tested the application by hand in the last 12 months
## Fix first
1. PHP is a version that still receives security fixes (check php.net/supported-versions.php). Once a branch reaches end of life, new flaws are never patched and public exploits stay usable.
2. Laravel (or your framework) is on a release that still gets security fixes. Older major versions stop getting fixes, and attackers read the fix notes of newer versions to attack the old ones.
3. composer audit runs in CI and a known advisory fails the build or is reviewed. It checks every package in composer.lock against published advisories before the code ships.
4. APP_DEBUG is false and APP_ENV is production on the live site. Debug pages show stack traces, file paths and configuration values, and have been used to run code on exposed sites.
5. The web server document root is the public folder, and nothing else in the project is reachable by URL. Otherwise .env, composer.json, storage and source files can be downloaded.
6. APP_KEY is set, unique to each environment and has never been committed or shared. It encrypts cookies and sessions. A leaked key lets an attacker forge them, and on some versions run code.
7. Every route that reads or changes a record checks that the signed-in user owns it (policies or gates). Broken access control, where changing an id in the URL shows another customer's data, is the most common serious bug.
8. Queries use Eloquent or bound parameters, and no user input is concatenated into raw SQL (whereRaw, DB::raw, selectRaw). String-built queries are the classic route to SQL injection and a database dump.
How it works
How the score works
Each item has a weight of 1, 2 or 3. The items with weight 3 are marked key. They are the gaps behind the largest share of real compromises: running a PHP version that no longer gets fixes, leaving debug mode on, serving the project folder instead of public, shipping known-vulnerable packages, missing access checks, raw SQL built from input and uploads that can run as PHP. The score is the weight of what you ticked divided by the total weight, so ticking easy items does not hide a serious gap.
The bands are 90 and above (strong), 70 to 89 (decent), 40 to 69 (exposed) and below 40 (high risk). They show urgency, not a guarantee. An app can score well and still carry a flaw in its own code, which is why the last items ask for a hand test.
Why these checks
PHP applications are mostly broken into through four routes: out-of-date PHP or packages with a public flaw, secrets and debug pages that were left reachable, application code that trusts input or skips an ownership check, and server settings that let an uploaded file run. The list is built around those routes, and each item names the way it goes wrong.
Several items can be checked by machine. composer audit finds known package flaws, and the Composer audit tool on this site does the same from a pasted lockfile. The PHP version checker shows whether your branch still gets security fixes and what a later release fixes. The wp-config and htaccess generators cover the equivalent items for WordPress and Apache.
Using the result
Start with the five items under Fix these first. Tick an item only when it is true today and you have checked it, not when you plan to do it. Copy the Markdown report to keep a record, and run the checklist again after a major upgrade or a new host. Your ticks are saved in this browser only, and nothing is sent anywhere.
Worked examples
A typical neglected app
Debug is off, the docroot is public and the key is set. Nothing else.
13% High risk (3 of 31 in place) Fix first: a supported PHP version, a supported framework release, composer audit in CI
The three basics are in place, but updates, access control and SQL handling are open.
A tidier app
The basics, plus supported PHP and framework, composer audit in CI, ownership checks, bound queries, fillable models, uploads kept from running, no dev tools in production, a locked install, tested backups, a private .env, a limited database user and https.
61% Exposed (16 of 31 in place) Fix first: replace abandoned packages, get advisory alerts, switch off display_errors
Strong code habits. What is left is housekeeping, and it is worth finishing because it is cheap.
What the report looks like
Copy the Markdown result.
# Laravel and PHP security checklist Score: 13% (High risk). 3 of 31 items in place. - [x] APP_DEBUG is false and APP_ENV is production - [ ] composer audit runs in CI...
Useful for a developer, a client or an insurer.
Frequently asked questions
Is Laravel secure by default?
What is the most common Laravel security mistake?
Does this work for Symfony or plain PHP?
How often should I run composer audit?
Do I need a web application firewall?
What if my score is low?
Can a checklist find a hacked app?
Keep reading
See what attackers see, for free
Run the free passive check on your website. No login, no impact on your site, results in seconds.
Ready for the full manual audit? See pricing