Securing a Node.js or Laravel app: the checks we run before launch
Custom applications get hacked through a different set of doors than WordPress: debug pages, leaked environment files, unpatched packages, missing rate limits. Here is the list we run through before any Node.js or Laravel app goes live.
Custom applications do not have the plugin problem WordPress has. They have their own problems, and they are less forgiving, because there is no ecosystem of security plugins to fall back on. Every hole is one someone on the project left open. This is the list we run before launch and again after any major change.
Configuration
Debug mode off in production. Laravel with APP_DEBUG=true shows a full stack trace, environment variables and database credentials to anyone who triggers an error. We have inherited a live app in exactly this state. In Node, the equivalent is error handlers that return stack traces, and NODE_ENV not set to production.
Secrets out of the code and out of reach. Environment files not committed to the repository, and not reachable over the web. A .env file at a public path has leaked credentials for a lot of applications. Check that the web server refuses to serve dotfiles.
HTTPS enforced, with the app aware it is behind a proxy so redirects and secure cookies work. Cookies set with HttpOnly, Secure and SameSite.
Security headers. Content security policy, frame options, content type options, referrer policy, HSTS. In Node the helmet package does this in one line. Laravel needs middleware or the web server.
Dependencies
Audit the packages. npm audit or composer audit, with the findings fixed rather than ignored. A production app with hundreds of dependencies has a new known vulnerability most months. Automate the check.
Pin and update the runtime. Node on a current LTS release, PHP on a supported version. An unsupported runtime undoes everything else.
Remove what is not used. Every package is attack surface and a maintenance cost.
Input and data
Validate everything at the boundary. Every request body, query string and upload validated against what you expect, with the rest rejected. Laravel’s form requests and validation rules, or a schema library like zod in Node.
Queries through the ORM or parameterised. No string-built SQL, ever. This is what stops injection, and it is easier than the alternative.
Uploads treated as hostile. Type checked by content not by name, size limited, stored outside the web root or in object storage, never executable, served with the right content type.
Output escaped. Templates escape by default in both ecosystems. The risk is the places someone turned escaping off to render HTML.
Authentication and access
Rate limiting on login, password reset, signup and any endpoint that costs money or sends mail. Laravel has throttling built in; Node needs a package or the edge.
Sessions and tokens that expire, rotate on login, and are invalidated on logout and password change.
Authorisation checked on every request, not only in the interface. The classic hole is an endpoint that returns any record by ID because the check was only in the front end. Test by changing IDs in requests while logged in as a different user.
Password hashing with bcrypt or argon2, and two-factor for administrator accounts.
CSRF protection on state-changing routes. Laravel ships it; make sure nobody excluded routes from it. In Node, a token approach or SameSite cookies with care.
Operations
Run as an unprivileged user, not root. Process manager that restarts on crash.
Logging that captures auth failures and errors, shipped somewhere off the server, without logging secrets or personal data.
Backups of the database on a schedule, tested by restoring.
A way to know when it is down and when errors spike.
The two we see most
Debug mode on, and an authorisation check that only existed in the interface. Both are one-line fixes, both are found by someone who is not the developer looking with fresh eyes. That is the point of running a list rather than trusting memory. Our scanner covers the outside layer for any site: headers, HTTPS, exposed versions. The inside layer is this list.
See where your own site stands.
The scanner checks the things this guide talks about, in about ten seconds, no signup.
More from the guides
Hacked WordPress: the cleanup order that holds
Most hacked sites get cleaned twice. The first time removes what you can see. Here is the full order we use so the second time is never needed: contain, find the door, clean, rotate, patch, watch.
Is my website hacked? Twelve signs, and a five-minute check
Most hacked sites look normal to their owners for weeks. The signs show up in Google, in your inbox and on other people's phones first. Here are the twelve we check, and a five-minute routine that catches most of them.
Deceptive site ahead: what it means and the fix order
Chrome is not warning about your design. Google Safe Browsing found phishing or malware on your domain. Here is what the red screen means, how to find the cause, and how to get the flag removed without it coming back.