Lately there's been a lot of noise about yet another Next.js CVE, about vulnerabilities in popular frameworks, about how "React is full of holes" and how we need to "flee to other technologies." The panic among developers is real. The question is — is it justified?
Before we dive in — one thesis I want to drill into your head from the start:
There is no secure framework. There are secure or insecure developers.
Okay, let's break this down properly.
AI Lowered the Bar. Significantly.
Five years ago, finding a serious vulnerability in a web application required real skill. Reverse engineering, knowledge of vulnerability patterns, manual fuzzing, years of experience. It was a club for the select few.
Today we have language models that can analyze code, find vulnerability patterns and suggest an exploit in minutes. We have tools that scan repositories, correlate dependencies with CVE databases and flag problems automatically. The barrier to entry for an attacker has dropped dramatically.
In short — back then, to hurt an application, you had to know what you were doing. Now you just need to know what you want.
That doesn't mean every script kiddie has suddenly become a pentester. But it does mean that the attack surface is now exposed to far more eyes. And those eyes are actively searching.
Modern Frameworks Are Complex. Complexity Is the Enemy of Security.
Remember the days of simple HTML with PHP? One file, one responsibility, zero magic. Today we have SSR, RSC (React Server Components), Server Actions, Edge Runtime, middleware, cache layers, hydration, ISR, PPR... and that's just in Next.js.
Every layer of abstraction is a potential place where something can go wrong. Not because the framework creators are careless — but because the complexity of the system grows faster than the ability to anticipate all edge cases.
Add to that the npm ecosystem with hundreds of thousands of packages, each with their own dependencies, their own maintainers and their own vulnerability history. One infected package a few levels deep in the dependency tree — and you have a supply chain attack in production.
The catch is that this isn't a problem with the framework itself. It's a problem with the scale and complexity of the ecosystem that grew around it.
Why Does Next.js Take the Most Heat?
I often hear: "Next.js is full of holes because Vercel doesn't care about security." That's not true and it needs to be said plainly.
Next.js gets a lot of CVEs for one main reason — it's the most popular SSR framework in the world. And popularity attracts attention. Both from security researchers who actively look for vulnerabilities (that's good), and from attackers (that's less good).
But there's another side to this — Next.js is exceptionally feature-rich. Server Actions, Edge middleware, its own cache, React Server Components integration. The more moving parts, the more potential points of contact between them where a bug can appear.
One recent high-profile case — a vulnerability in Next.js middleware that, with a specially crafted header, allowed bypassing authorization entirely. It wasn't a React bug. It was a bug in a very specific place, in a very specific configuration. But it was enough to set half of Twitter on fire.
Either way — Next.js is popular, complex and actively analyzed. That's the price of being the leader.
Top 20 Threats in Development
Okay, time for specifics. Below is my top 20 list of the biggest threats and worst practices (from my full list of exactly 100 things to watch out for) that make you easy prey for these kinds of attacks and vulnerability exploitation.
1. Missing lockfile or ignoring it in CI Using npm install instead of npm ci in a pipeline is a classic. The build isn't reproducible — today you install one version of a dependency, tomorrow another. With a bit of bad luck — a malicious one.
2. Automatic merge of dependency updates without review Dependabot and Renovate are great. Automerge without tests and code review — not so much. A broken or malicious package version can hit production faster than anyone notices.
3. Overly loose package versions "package": "*" or "package": "latest" in package.json is asking for trouble. The ^ alone isn't an absolute evil, but without a lockfile and update controls it gets dangerous.
4. Installing small, random packages from npm A package for one function, a small helper, a random UI component found on GitHub. Every package is an additional maintainer, additional dependencies and additional attack surface.
This is a particularly interesting angle — attacks don't go through npm, they go through the developer's environment. Detections of malicious VS Code extensions nearly quadrupled in 2025 — from 27 cases in 2024 to 105 in the first ten months of 2025.

In November 2025, an extension called prettier-vscode-plus appeared — impersonating the popular Prettier. After installation it launched a multi-stage attack chain ending in full remote access to the developer's machine, theft of tokens, SSH keys and CI/CD secrets.
5. No review of lockfile changes In a PR everyone looks at the components, while package-lock.json has 4,000 lines and nobody reads it. Yet that's exactly where you'd see new transitive dependencies or suspicious version substitutions.
A recent example — September 2025: an attack on the maintainer accounts of chalk, debug, ansi-styles and 15 other packages with a combined reach of over 2.6 billion weekly downloads. The attack vector was trivial — phishing impersonating npm support, which tricked a maintainer into resetting their credentials and 2FA. Every project with automerge pulled in the malicious version automatically within those 2 hours.
6. Secrets in the frontend NEXT_PUBLIC_* and VITE_* are public. I know that sounds obvious. And yet apparently every week someone pushes an API token in an env variable to production, where it ends up in the client bundle.
7. Tokens in localStorage Convenient, popular and risky. With XSS, an attacker can steal the token and hijack the session. A much safer model is HttpOnly, Secure, SameSite cookies.
8. Rendering HTML/Markdown without sanitization dangerouslySetInnerHTML, product descriptions from a CMS, user comments, WYSIWYG. Without sanitization, this is a classic gateway for XSS.
A well-known example — British Airways, 2018. Attackers got in through compromised credentials of an external vendor, modified a JavaScript file on the site and for 16 days a skimmer was collecting payment card data from customers. The ICO imposed a fine of £20 million.
9. Missing Content Security Policy CSP doesn't fix XSS, but it dramatically limits its impact. Without CSP, a single bug in content rendering can allow malicious JavaScript to execute on the page.
10. Relying solely on frontend validation Frontend validation is for UX, not security. A user can bypass the UI and send a request manually. The backend must validate data, roles, limits and ownership. Always.
11. Missing backend authorization for resources Hiding the "Delete" button in React doesn't secure the endpoint. IDOR, privilege escalation, access to other people's data after changing userId in a parameter — classics that are still very much alive.
12. Incorrect caching in SSR/Next.js Next.js aggressively caches Server Component and Route Handler responses. The problem appears when you forget to mark user-specific data as dynamic.
Example: you have a Server Component that fetches data for the logged-in user. Without an explicit cache: 'no-store' or cookies()/headers() inside, Next.js may cache that response. The next user visits the same page and gets... the previous user's data. Their name, orders, address.
This is not a theoretical scenario — it's a real bug that appears when you write Server Components like regular components, without thinking that Next.js cache works differently from browser cache. I fell victim to it myself early in my Next.js journey.
13. Public source maps in production Source maps help with debugging locally. Publicly — they reveal application structure, function names, business logic and endpoints. They make reverse engineering significantly easier.
And you don't have to look far for examples. In March 2026, Anthropic accidentally published the full source of Claude Code to npm — 512,000 lines of TypeScript — through a source map file (.map) included in package version 2.1.88. At the same time it became clear just how chaotic a piece of software Claude Code actually is.

14. Logging sensitive data console.log(user), console.log(token), full API responses in Sentry. Tokens, personal data and API keys end up in logs that often have broad access within the team.
15. Unsafe redirects A post-login redirect to a URL taken without validation from a next parameter. Open redirect, phishing, hijacking the login flow — and suddenly a simple "return after login" feature becomes a problem.
16. Missing tests for critical paths Most projects have tests — the problem is what gets tested. We check whether a component renders, whether a button has the right color, whether a modal opens on click. That's easy to write and gives green checkmarks in CI.
Meanwhile nobody wrote a test that checks whether a user without an admin role truly cannot delete someone else's order. Or whether the cart state returns to normal after a failed payment. Or whether a password reset actually invalidates the old token after use.
The result: regressions in these places surface in production, not in the pipeline. And these happen to be the places where a bug costs — financially, reputationally or legally.
17. Running PR code with access to CI/CD secrets Especially in open source or with forks. A malicious PR can extract secrets from the pipeline before anyone does a review.
18. Overly broad CI/CD token permissions A deploy token with access to everything. After a single secret leaks — access to package publishing, deploys or the entire infrastructure.
19. No control over third-party scripts Every external script — analytics, chat widget, advertising pixel — loaded via <script> has full access to your application's DOM, forms and storage, exactly like your own code. The problem isn't even with the vendors themselves, because reputable companies are usually fine — it lies in their dependencies and in the fact that domains and packages change owners. A classic example: Polyfill.io, a script loaded by hundreds of thousands of sites, started serving malicious code to users after the domain was sold — with no change whatsoever to those sites' own code.
20. Putting off framework and package updates for years Complete lack of updates is asking for trouble — old CVEs, dead libraries, migrating across three major versions at once when something finally forces your hand. But automatic updates without review is the other extreme of the same stupidity. The best approach: Dependabot opens PRs, CI validates them, a human makes the decision.

Maybe Just Switch Frameworks?
Sure, I get that impulse. If Next.js has CVEs, maybe Astro or SvelteKit will be more peaceful? Let's look at the real alternatives.
Astro
Astro plays a completely different game. Zero JavaScript on the client by default — the so-called islands architecture. You ship static HTML and add interactivity only where you actually need it. You can mix React, Vue and Svelte in one project.
For: very small bundle, excellent Lighthouse scores out of the box, great integration with headless CMSes (Sanity, Contentful), smaller attack surface simply through the absence of client-side JS.
Against: with complex, heavily interactive applications you start fighting the tool instead of working with it. No full SSR on the level of Next.js. Smaller e-commerce integration ecosystem.
For whom: marketing sites, blogs, landing pages, portals with lots of static content. For those use cases, Astro is the best choice.
TanStack Start
Built by Tanner Linsley — creator of react-query and react-table, which sit inside half the React ecosystem. TanStack Start is a full SSR/SSG framework, built with an obsession for typesafety and without hidden logic. Zero "magic," everything clear and predictable.
For: exceptional typesafety from the ground up, routing you actually understand, a strong community around the TanStack ecosystem.
Against: still young — don't take it into a large production project without being aware of the risk. The integration ecosystem (CMS, e-commerce, auth) hasn't grown to the level of Next.js yet.
For whom: new projects where you can accept the early adopter risk. Web applications and dashboards where typesafety is critical.
SvelteKit
Svelte compiles to pure vanilla JS — no virtual DOM, smaller bundle, faster runtime. SvelteKit is a full SSR framework on top of Svelte with its own routing and conventions.
For: a different level of runtime performance, very readable code, minimal boilerplate, actively developed, growing popularity.
Against: you need to learn Svelte — it's a different world from React. Smaller ecosystem, fewer ready-made integrations, fewer specialists on the market if you're building a team.
For whom: projects where you have time to get into a new technology and care about performance and code quality. Great for greenfield projects.
Remix / React Router v7
After the merger, Remix and React Router are now one thing. A data model based on loaders and actions, very close to web standards (Web Fetch API), great for applications with heavy form usage and data mutations.
For: a very good UX model, progressive enhancement works out of the box, React under the hood so the same skillset applies.
Against: smaller ecosystem than Next.js, fewer hosting providers support it natively, some conventions require a mental shift.
For whom: applications with heavy data fetching and mutations, e-commerce where progressive enhancement matters.
So Should We Be Scared?
No. But we should be serious.
Despite the recent CVEs, despite the growing number of attacks, despite AI having lowered the bar for attackers — the risk is manageable. Provided we treat security as part of the process, not an afterthought.
A few concrete things you can implement tomorrow:
- Configure Dependabot or Renovate with a code review requirement instead of automerge
- Enable GitHub Security Alerts or the equivalent in your repo
- Subscribe to security advisories for the frameworks you use — Next.js has an official channel, most large projects do too
- Set
npm auditor the equivalent as a step in CI/CD — if the audit fails, the build fails. Though that's a fairly drastic step. - Set up CSP at least in report-only mode to start

Switching frameworks won't fix security problems if the processes in your team are broken. Astro with terrible CI/CD will be less secure than Next.js with proper alerts and a culture of review.
Risk always exists — regardless of framework, language or cloud provider. The difference is in how actively you manage that risk.
And Next.js, despite all the CVEs from recent months, has one argument in its favor — it's actively maintained, vulnerabilities are patched quickly, and the Vercel team approaches security advisories professionally. This isn't an abandoned framework. It's a framework that takes hits precisely because it's everywhere.
Keep an eye on your packages. Update regularly. Monitor alerts. And maybe don't put off that Next.js update "until next week."


