By Shyam Verma

How to Fix & Prevent React2Shell Attack on Next.js

How to Fix & Prevent React2Shell Attack on Next.js

On December 3, 2025, React2Shell dropped - a CVSS 10.0 vulnerability letting attackers run any code on your Next.js server. No login needed. Within hours, state-sponsored groups were exploiting it.

This guide shows exactly how I secured a production app, with commands you can copy-paste.

TL;DR - Quick Fix

If you're in a hurry, run this now:

# Check if you're vulnerable
npm list next

# Fix it
npx fix-react2shell-next

# Verify
npx fix-react2shell-next
# Should say: "Your Next.js installation is not vulnerable"

Then read the rest of this guide because patching alone isn't enough.


Am I Affected?

Quick check:

  1. Using App Router? (files in /app directory) → Potentially vulnerable
  2. Using Pages Router only? (files in /pages directory) → Safe
  3. Static export? (no server) → Safe
  4. Next.js 13.x or 14.x stable? → Safe

Vulnerable versions:

Version Patch to
15.0.x 15.0.5+
15.1.x 15.1.9+
15.2.x 15.2.6+
15.3.x 15.3.6+
15.4.x 15.4.8+
15.5.x 15.5.7+
16.0.x 16.0.7+

Full details: Next.js Security Advisory


What's the Actual Risk?

React2Shell exploits a flaw in how React Server Components handle incoming data. An attacker sends a malformed request, and your server executes their code.

What attackers can do:

  • Read your environment variables (API keys, database passwords)
  • Access your database
  • Pivot to other systems on your network
  • Install crypto miners or backdoors

For the technical deep-dive: Datadog's analysis or Wiz's breakdown.


Step 1: Patch Now

The critical fix. Do this first.

# Option A: Use the official tool
npx fix-react2shell-next

# Option B: Manual upgrade
npm install next@15.5.7  # or latest in your branch

Verify it worked:

npx fix-react2shell-next
# Expected: "Your Next.js installation is not vulnerable to CVE-2025-55182"

Step 2: Scan for Compromise

Before assuming you're safe, check if you were already exploited.

Assetnote's scanner can detect vulnerable instances:

# Clone and install
git clone https://github.com/assetnote/react2shell-scanner
cd react2shell-scanner
pip install -r requirements.txt

# Safe scan (no code execution on target)
python3 scanner.py -u https://your-app.com --safe-check

Check your logs for:

  • Unusual POST requests to /_next/ paths
  • Multipart requests to non-upload endpoints
  • Spikes in 500 errors

Step 3: Hide Your Tech Stack

Attackers scan for Next.js servers by checking response headers. Remove the fingerprints.

In next.config.ts:

const nextConfig: NextConfig = {
  headers: async () => [
    {
      source: '/(.*)',
      headers: [
        { key: 'X-Powered-By', value: '' },
        { key: 'Server', value: '' }
      ],
    },
  ],
};

If using a reverse proxy (Nginx/Caddy):

# Nginx
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
# Caddy
servers {
    headers {
        -Server
        -X-Powered-By
    }
}

Verify:

curl -I https://your-app.com 2>/dev/null | grep -E "Server:|X-Powered-By:"
# Should return nothing

Step 4: Rotate All Secrets

This is the step most people skip. Don't.

Even if you patched quickly, your secrets may have been stolen during the vulnerability window. Assume breach.

What to rotate:

Category Examples
Database Connection strings, passwords
APIs Stripe, OpenAI, Mailjet, Vimeo
Auth JWT secrets, session keys
CMS Admin tokens, API keys
Webhooks Signing secrets

Rotation process:

  1. Generate new credentials in service dashboard
  2. Update .env.production
  3. Deploy with new credentials
  4. Verify the app works
  5. Then revoke old credentials

Quick validation script:

// test-credentials.ts
async function test(name: string, fn: () => Promise<boolean>) {
  try {
    const ok = await fn();
    console.log(ok ? `✓ ${name}` : `⚠ ${name}: Check permissions`);
  } catch (e) {
    console.log(`✗ ${name}: ${e.message}`);
    process.exit(1);
  }
}

// Add tests for each service
await test('Database', async () => {
  // Your DB connection test
  return true;
});

await test('Stripe', async () => {
  const res = await fetch('https://api.stripe.com/v1/balance', {
    headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` }
  });
  return res.ok;
});

Run after each deployment: npx tsx test-credentials.ts


Step 5: Lock Down API Access

Where possible, restrict your API keys:

  1. Use restricted keys - Stripe, for example, lets you create keys with specific permissions only
  2. IP allowlisting - Some services let you restrict API access to your server IPs
  3. VPN for admin panels - Put sensitive dashboards behind a VPN

Step 6: Fix Container Permissions

If you're running Docker, check your file permissions. Common issue: upload directories owned by wrong user.

# Set correct ownership (adjust path and UID for your setup)
sudo chown -R 1000:1000 /var/data/uploads
sudo find /var/data/uploads -type d -exec chmod 755 {} \;
sudo find /var/data/uploads -type f -exec chmod 644 {} \;

Add this to your deployment script so it runs automatically.


Verification Checklist

Run through this after remediation:

# 1. Version check
npm list next
# Should show patched version

# 2. Official verification
npx fix-react2shell-next
# Should say "not vulnerable"

# 3. Header check
curl -I https://your-app.com | grep -E "Server:|X-Powered-By:"
# Should return nothing

# 4. Credential test
npx tsx test-credentials.ts
# All should pass

Copy-Paste Checklist

Save this for your incident response:

Immediate (Do Now)

  • npx fix-react2shell-next
  • Verify: npm list next shows patched version
  • Check logs for exploitation attempts

Today

  • Hide server headers (Next.js config + reverse proxy)
  • Scan with react2shell-scanner
  • Start secret rotation

This Week

  • Complete all secret rotations
  • Revoke old credentials (after verifying new ones work)
  • Review container permissions
  • Set up log monitoring for suspicious patterns
  • Restrict API keys where possible

Key Lessons

  1. Patch fast, but don't stop there - The patch fixes the vulnerability, but doesn't undo any damage from the exposure window.

  2. Assume breach - When a CVSS 10.0 exploit goes public, assume attackers tried it on you. Rotate secrets.

  3. Layer your defenses - Hide server signatures, restrict API access, monitor logs. No single control is enough.

  4. Automate verification - Write scripts to test credentials and security controls. Run them in CI/CD.

  5. Stay informed - Follow Next.js blog, GitHub Security Advisories, and your framework's security channels.


Questions? Reach out on Twitter @ssv445 or LinkedIn.

Interested in Collaborating?

Whether it's a startup idea, a technical challenge, or a potential partnership—let's have a conversation.

20+
Years Experience
150K+
Websites Powered
2
Successful Exits
7x
Faster with AI