DEV Community

Cover image for Solved: Taking down Next.js servers for 0.0001 cents a pop
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: Taking down Next.js servers for 0.0001 cents a pop

🚀 Executive Summary

TL;DR: Next.js servers are susceptible to denial-of-service attacks and inflated cloud bills by exploiting the default image optimization endpoint to process arbitrary external images. The primary solution involves whitelisting allowed image source domains using remotePatterns in next.config.js or implementing a WAF/CDN for edge-level rate-limiting. This prevents the server from downloading and processing malicious or oversized files, securing infrastructure and controlling costs.

🎯 Key Takeaways

  • The Next.js Image Optimization API (/\_next/image) can be exploited by instructing it to fetch and process images from any external URL, leading to high CPU/memory usage and significant cloud costs.
  • Implementing remotePatterns in next.config.js is the essential application-level fix, explicitly whitelisting approved image source domains and preventing the server from processing non-whitelisted external URLs.
  • Deploying a Web Application Firewall (WAF) or CDN with rate-limiting rules (e.g., on /images/\_next/\*) provides an infrastructure-level defense, blocking malicious traffic at the network edge before it impacts Next.js servers.

A malicious actor can cripple your Next.js server and run up your cloud bill by exploiting the default image optimization endpoint. Here’s how we stop the bleeding, fix the root cause, and harden your infrastructure for good.

That Next.js “Feature” Costing You Thousands? Let’s Fix It.

I remember getting the PagerDuty alert at 2:17 AM on a Sunday. CPU utilization on our new marketing fleet was pegged at 99% for over an hour. My first thought was a bad deploy or an infinite loop in the code. But when I SSH’d into one of the boxes, htop told a different story. It wasn’t our Node process going wild; it was dozens of sharp image processing threads, all spun up by Next.js, all trying to download and resize a multi-gigabyte astronomical survey image from some obscure university’s FTP server. Someone found our /\_next/image endpoint and was having a field day. Our cloud bill for that little “incident” was four figures. This isn’t a theoretical problem; it’s a financial landmine waiting for you to step on it.

The “Why”: An Open-Door Policy for Images

Let’s be clear: this isn’t a “vulnerability” in the traditional sense. It’s a powerful feature with default settings that are, in my opinion, dangerously permissive. The root cause is the Next.js Image Optimization API, exposed at /\_next/image. By default, it can be instructed to fetch an image from any URL on the internet. An attacker simply crafts a URL like:

https://your-site.com/_next/image?url=https://some-shady-site.com/huge-image.tiff&w=3840&q=100
Enter fullscreen mode Exit fullscreen mode

When your server receives this request, it obediently:

  1. Downloads the massive file from the external URL.
  2. Spends significant CPU and memory resizing it.
  3. Serves the result back to the attacker.

Multiply this by a few thousand requests from a simple script, and your servers are toast, and your cloud provider is sending you a very large invoice. You’re paying for the bandwidth, the compute, and the egress. It’s a denial-of-service attack you pay for.

The Fixes: From Band-Aid to Body Armor

Okay, enough with the horror stories. Let’s get this locked down. I’ve got three tiers of solutions, from the immediate fix you should deploy five minutes ago to the long-term architectural shield.

Solution 1: The Quick Fix (The Configuration Whitelist)

This is the first thing you should do. It’s built right into Next.js, but it’s tragically easy to miss during initial setup. You need to explicitly tell Next.js which domains are allowed to be sources for your images. You do this in your next.config.js file.

Instead of leaving it wide open, you define a strict list of sources using remotePatterns. This is the modern, more flexible way to do it.

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'assets.your-company-cms.com',
        port: '',
        pathname: '/images/**',
      },
      {
        protocol: 'https',
        hostname: '**.s3.amazonaws.com',
      },
    ],
  },
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

With this in place, if an attacker tries to use a non-whitelisted domain, Next.js will throw a 400 Bad Request and never even attempt to download the image. The attack is stopped at the application layer before it can consume resources.

Solution 2: The Permanent Fix (The Infrastructure Shield)

Configuring Next.js is essential, but as a cloud architect, I don’t like relying solely on the application for security. We need defense in depth. This is where you put a shield in front of your entire application: a Web Application Firewall (WAF) and a CDN with rate-limiting capabilities, like AWS WAF or Cloudflare.

We configure a specific rule that targets the image optimization path. For example, in Cloudflare, we can set a rate-limiting rule on any request to /images/\_next/\* that allows, say, 100 requests per IP per minute. Any IP address that exceeds this is automatically blocked for a period of time.

Pros Cons
Blocks malicious traffic before it even hits your Next.js server, saving compute resources. Can add a small amount of cost and complexity to your stack.
Protects against more than just this one attack vector (e.g., DDoS, SQL injection). Requires careful configuration to avoid blocking legitimate, high-traffic users.
Globally distributed, so the block happens at the edge, close to the attacker. Another piece of infrastructure to manage and monitor.

This approach moves the fight from your application servers (e.g., prod-web-app-01) to the global network edge. Your app never even knows it was under attack.

Solution 3: The ‘Nuclear’ Option (Disabling the Beast)

Let’s say you’re under a massive, sustained attack. The bill is climbing, and you need to stop the bleeding right now. You don’t have time to test and deploy a WAF rule. In this emergency scenario, you can disable the Next.js Image Optimization feature entirely.

You do this with a one-line change in next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    unoptimized: true,
  },
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

This is the “pull the fire alarm” option. It’s hacky but effective. Once deployed, Next.js will serve all images as-is from their source URL without any processing. The /\_next/image endpoint effectively goes dead.

Warning: This is a drastic measure. Disabling optimization will likely harm your site’s performance, hurt your Core Web Vitals, and could negatively impact your SEO. Use this only as a temporary emergency stopgap while you implement Solution 1 or 2.

Look, we all love frameworks that make our lives easier, but convenience can’t come at the cost of stability and security. This Next.js image issue is a classic example. It’s a powerful tool, but with great power comes the responsibility to configure it correctly. Don’t wait for that 2 AM alert. Check your config, shield your infrastructure, and sleep well at night.


Darian Vance

👉 Read the original article on TechResolve.blog


☕ Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)