Compromised Node.js or Next.js Site: Where to Look
By Glenn Lyvers · Updated · 4 min read
Every hacked-site guide on the internet tells you to look for PHP files in your uploads folder. On a Node application there is no uploads folder and no PHP, and the habits that work for WordPress do not transfer. The attack surface moved somewhere else entirely — mostly into your dependency tree and your deployment pipeline.
Your dependency tree is the main surface
A typical Node application pulls in hundreds of packages, most of them indirectly, written by people you will never identify. Any one of them can be compromised at the source, taken over after a maintainer hands it off, or published under a name close enough to a real package that someone installs it by accident.
Start here, because it is both the most likely route and the easiest to check. Run an audit against your lock file to see which dependencies carry known vulnerabilities, review anything added recently, and look for packages whose names are near-misses of ones you meant to install. Install scripts are worth particular attention, since a package can run arbitrary code at install time on whatever machine performs the install.
Environment variables and secrets
Node applications keep their secrets in environment variables, and the recurring failure is exposing them. On a framework with a client and server split, the classic mistake is prefixing a secret so that it gets bundled into the client-side JavaScript, where it is readable by anyone who views the page source.
Check what is actually in your client bundle. Search the built output for anything resembling a key or a credential. Then check for an environment file committed to version control or left in a web-reachable location. My guide on exposed environment files covers the rotation if you find one.
The build and deployment pipeline
This is the route people think about least and it is one of the more damaging. If your application is built and deployed automatically, whoever controls that pipeline controls what runs in production — without ever touching your server.
Check who has access to your repository and your hosting or platform account. Check for workflow or pipeline configuration files you did not write, and read the ones you did for modifications. Check deploy keys, access tokens and integrations, since those are credentials that survive password changes. A malicious build step is effectively invisible from the running application, which is exactly what makes it worth checking.
The running application
Compare the deployed code against a known-good commit, which is the fastest and most definitive check available on a version-controlled application. Anything that differs from what you intended to deploy is a finding, and the diff gives you the whole answer at once.
Beyond that: check your routes and API endpoints for additions, check middleware for changes, and look at what your process is actually doing — unexpected outbound network connections and unexplained CPU use are the classic signs of a cryptominer, which my guide on cryptomining malware covers in a different context. Check server-side rendering paths for injected output if the symptom is something visitors see.
Cleaning it
Rebuild from a known-good commit rather than repairing the running deployment, and reinstall dependencies from a lock file you have reviewed rather than letting resolution pick fresh versions. Rotate every secret: database credentials, API keys, tokens, session secrets and anything else in your environment.
Then rotate the pipeline credentials too — deploy keys, personal access tokens, platform API keys and any third-party integration with repository access. Those are the ones that let someone come straight back, and they are routinely forgotten because they are not passwords anyone types.
Reducing the risk
Pin dependencies with a committed lock file, review what a dependency update actually brings in rather than accepting it blindly, and run automated vulnerability auditing as part of your build. Keep the number of direct dependencies smaller than habit suggests, because every one of them carries its own tree.
Protect the pipeline like production, since it is production: two-factor authentication on the repository and hosting accounts, narrowly scoped deploy tokens, required review on workflow changes, and periodic pruning of access for people who have moved on. If you want a compromised application assessed properly rather than guessed at, that is work I take on — see the platforms I clean.
Common questions
Where does malware hide in a Node application?
Most often in the dependency tree rather than in code you wrote — a compromised or typosquatted package, possibly running code at install time. After that, in the build pipeline, and in the running deployment if someone had direct access. There is no uploads folder equivalent to sweep.
How do I check my dependencies?
Run an audit against your lock file to surface known vulnerabilities, review anything added recently, and look for package names that are near-misses of ones you meant to install. Pay particular attention to install scripts, since those execute during installation on whatever machine builds your app.
Could my API keys be visible in the browser?
Yes, and it is a common mistake. On frameworks with a client and server split, prefixing a variable so it is exposed to the client bundles it into JavaScript that anyone can read. Search your built output for anything resembling a credential before assuming it is safe on the server.
Why does the build pipeline matter?
Because whoever controls it controls what runs in production without ever touching your server. A malicious build step is invisible from the running application. Check pipeline configuration files, deploy keys and access tokens, and treat that account with the same seriousness as production itself.
What is the fastest way to tell if the code was modified?
Diff the deployed code against a known-good commit. On a version-controlled application that gives you a definitive list of what differs from what you intended to ship, which is far better than the inference-based checks you are stuck with on a typical CMS site.
More than malware
Most people meet me in an emergency. It isn’t all I do.
I’ve been building and repairing systems since 1995. Whatever brought you here, there’s a good chance I can help with the rest of it too — and you’ll be dealing with the same person either way.
Hacked, but not WordPress?
Joomla, Drupal, Magento, Shopify, PrestaShop, Laravel, Node, IIS and plain HTML — cleaned the same way, priced the same way.
Take a look →Custom builds & AI systems
Plugins, custom applications, website chatbots and automation — built to do exactly what you need, maintained by the person who wrote them.
Take a look →Servers, speed, SEO & accessibility
Migrations, faster load times, technical SEO and accessibility fixes. Measured improvements, with the numbers to show you.
Take a look →Better web hosting
Fast, secure hosting with SSL and backups included at no extra charge. Clear pricing, no long-term contracts, no surprises.
Take a look →Classes & free tools
Rather learn to handle it yourself? I teach this, and I give away the tools I built for my own cleanups.
Take a look →Something else broken?
Half my work is untangling what someone else started, gave up on, or broke. Describe it in plain words and I’ll tell you honestly.
Take a look →Tell me what’s wrong. I’ll tell you what it takes.
No queue, no call centre, no sales pitch — one person who answers, quotes honestly, and does the work.