How to Fix: API Routes Responses Not Being Gzipped in Next.js App Router

5 min read

Next.js App Router API responses are not gzipped in development because compression is intentionally skipped under next dev, which makes the behavior look broken even though production usually works as expected.

If you create a Route Handler in the App Router and test it locally, you may notice that the response has no Content-Encoding: gzip header. This is especially confusing when older pages, proxies, or production deployments appear compressed. The key detail is that development mode and production mode do not use the same response pipeline.

Understanding the Root Cause

In Next.js App Router, API endpoints are implemented as Route Handlers such as app/api/example/route.ts. When you run the app with next dev, Next.js prioritizes fast rebuilds, debugging visibility, and a simpler local server pipeline. As a result, response compression is typically not applied in development.

This means your Route Handler can return valid JSON, text, or streamed output, but the local development server may send it uncompressed even if the browser or client includes Accept-Encoding: gzip, br.

Why this happens technically:

  • Development server behavior differs from production: Next.js does not try to fully mirror production optimizations while running next dev.
  • Compression is an optimization layer: gzip and brotli are generally added by the production server, reverse proxy, CDN, or hosting platform.
  • App Router uses Web Response APIs: Route Handlers return a Response object, and compression depends on the server runtime around that response, not just your handler code.
  • Streaming and hot reload concerns: in development, disabling compression can make diagnostics and incremental updates easier.

So the issue is usually not a bug in your route code. It is most often a mismatch between expecting production behavior while testing in development.

Step-by-Step Solution

The correct fix is to verify compression in a production build, not in next dev.

1. Create a simple App Router API route

import { NextResponse } from 'next/server'

export async function GET() {
  const payload = {
    message: 'Hello from App Router',
    items: Array.from({ length: 5000 }, (_, i) => `item-${i}`),
  }

  return NextResponse.json(payload)
}

Save it as:

app/api/test/route.ts

2. Start development mode and observe the missing gzip header

npm run dev

Then test with curl:

curl -i -H "Accept-Encoding: gzip" http://localhost:3000/api/test

You may see that Content-Encoding is missing. In development, that is expected.

3. Build and run the app in production mode

npm run build
npm run start

Test the same endpoint again:

curl -i -H "Accept-Encoding: gzip" http://localhost:3000/api/test

Now check whether the response includes a compression header such as Content-Encoding: gzip or another supported encoding.

4. Confirm your Next.js compression setting

If you are running a standalone Node server, check your next.config.js:

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

module.exports = nextConfig

This option should normally be enabled by default, but explicitly setting compress: true removes ambiguity.

5. If you deploy behind a proxy or platform, verify where compression is handled

Many deployments do not rely on the Next.js process itself for compression. Instead, gzip or brotli may be added by:

  • Nginx
  • Vercel Edge Network
  • Cloudflare
  • A load balancer or CDN

In those setups, test the deployed URL rather than assuming local Node behavior will match it.

6. Do not manually add a fake gzip header unless you also encode the body

This is incorrect:

export async function GET() {
  return new Response(JSON.stringify({ ok: true }), {
    headers: {
      'Content-Type': 'application/json',
      'Content-Encoding': 'gzip',
    },
  })
}

Adding Content-Encoding: gzip without actually compressing the bytes will break clients. The server or proxy must perform the encoding step.

7. If you truly need local compression testing, use a production-like server path

Instead of depending on next dev, test with:

npm run build
npm run start

Or place a local reverse proxy in front of your app and enable compression there.

Common Edge Cases

Small payloads may not be compressed

Even in production, extremely small responses may be sent without gzip because compression overhead can outweigh the benefit. Test with a larger JSON payload before concluding compression is broken.

Streaming responses may behave differently

If your Route Handler uses streaming, compression behavior depends on the runtime and proxy configuration. Some platforms buffer, some stream, and some selectively compress only certain content types.

Compression may be handled by the hosting platform, not Next.js

If you deploy to a managed host, local behavior from next start may still differ from the live environment. Always verify response headers in the actual deployment.

Accept-Encoding must be sent by the client

Compression is usually negotiated. If your client does not send Accept-Encoding, the server may return plain content.

Custom servers can change the behavior

If you wrap Next.js with Express, Fastify, or another custom Node server, compression may depend on your middleware stack rather than Next.js defaults.

Binary and already-compressed content may not be gzipped

Formats like images, zip files, and some binary outputs are often skipped because they are already compressed or not worth compressing further.

FAQ

Why is my App Router API route not gzipped during next dev?

Because Next.js development mode does not guarantee production optimizations, and compression is commonly omitted to keep the dev server simpler and faster.

Is this an App Router bug or expected behavior?

In most cases, it is expected behavior. The Route Handler is not responsible for transport compression; the surrounding server infrastructure is.

How should I properly test gzip for a Next.js API route?

Run a production build with next build and next start, then inspect the headers with curl or browser dev tools. For deployed apps, verify the final response through your actual hosting stack.

The practical takeaway is simple: do not use next dev as the source of truth for gzip behavior. For Next.js App Router API routes, compression is a deployment/runtime concern, and the reliable fix is to validate it in a production-like environment.

Leave a Reply

Your email address will not be published. Required fields are marked *