Skip to article
PerformanceJun 25, 20254 min read

HTTP/2 and Brotli in Next.js: setup and verification

Check HTTP/2 and response compression in a Next.js deployment. Distinguish HTTPS from HTTP/2 and prefetching from Server Push, then measure the result.

HTTP/2 and Brotli in Next.js: setup and verification

HTTP/2 and compression can reduce resource delivery time and help loading metrics such as LCP. They do not, by themselves, fix layout shifts or guarantee better search rankings. Measure network transfer, rendering and page behavior separately.

HTTP/2, HTTPS and hosting

HTTP/2 multiplexes requests over a connection, reducing HTTP/1.1 request-level blocking. It still uses TCP, so packet loss can stall streams at the transport layer. HTTP/3 is already in use and addresses transport differently; choose based on measured behavior and hosting support.

Browsers use HTTP/2 over TLS. On managed hosting such as Vercel, check the protocol negotiated at the edge. For self-hosting, a reverse proxy or CDN can terminate TLS and HTTP/2 in front of the Next.js server. A custom Node.js https server does not enable HTTP/2 merely by using HTTPS.

For a local HTTPS experiment, generate a certificate for localhost. This is not a production certificate:

openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost' -addext 'subjectAltName=DNS:localhost'

The following custom server demonstrates HTTPS with HTTP/1.1, not an HTTP/2 deployment. It requires an existing Next.js project; production mode also requires a completed build. Prefer the normal Next.js server behind a proxy unless a custom server is needed.

import next from 'next'
import fs from 'node:fs'
import https from 'node:https'

const app = next({ dev: process.env.NODE_ENV !== 'production', port: 3000 })
const handle = app.getRequestHandler()

async function startServer() {
	await app.prepare()

	https
		.createServer(
			{
				cert: fs.readFileSync('./cert.pem'),
				key: fs.readFileSync('./key.pem'),
			},
			async (request, response) => {
				await handle(request, response)
			},
		)
		.listen(3000)

	console.info(`Ready on https://localhost:3000`)
}

startServer()

Verify the negotiated protocol and compression

Check the Protocol column in Chrome DevTools Network. With an HTTP/2-capable curl build, the first command prints the protocol actually used; requesting HTTP/2 can still result in fallback. The second command inspects a GET response after advertising Brotli and gzip support.

curl --http2 -sS -o /dev/null -w '%{http_version}\n' https://your-site.com
curl -sS -D - -o /dev/null -H 'Accept-Encoding: br, gzip' https://your-site.com

Brotli and gzip

With next start, Next.js provides gzip compression by default. Brotli may produce smaller text responses, but the saving depends on the data and settings; there is no universal 20–25% gain. On managed hosting, inspect actual responses. With self-hosting, configure Brotli at a proxy or CDN that supports it.

Disable Next.js compression only after another layer is configured to compress the responses. Otherwise keep the default. This configuration delegates compression; it does not enable Brotli:

// next.config.mjs
export default {
  compress: false,
}

A Brotli-encoded response may include:

Content-Encoding: br
Vary: Accept-Encoding

Do not set Content-Encoding manually on an uncompressed body. Check compressed and fallback variants and the cache’s handling of Accept-Encoding.

The following figures are illustrative, not measurements or a guaranteed compression ratio:

FormatOriginal JavaScriptCompressed size in the example
Gzip100 KB30 KB
Brotli100 KB22 KB

Prefetching is different from Server Push

Next.js Link can prefetch route resources. This is a browser request, not HTTP/2 Server Push. Chrome disabled Server Push by default starting with version 106; it should not be a dependency of a current optimization plan.

import Link from 'next/link'

export default function AboutLink() {
  return <Link href="/about" prefetch>About</Link>
}

For a confirmed critical font, a preload hint may be appropriate. Replace the example URL with the real resource and match its type and cross-origin settings:

<link rel="preload" href="/fonts/site.woff2" as="font" type="font/woff2" crossorigin>

Prefetch and preload consume bandwidth. There is no universal rule of five or six resources: use them only where the measured navigation or loading benefit justifies the work.

Image optimization

next/image can serve optimized formats according to configuration and the browser’s Accept header. WebP is the default output format; AVIF requires appropriate configuration. Keep dimensions to reserve space and reduce layout shifts.

import Image from 'next/image'

export default function ExampleImage() {
  return <Image src="/example.jpg" alt="Example" width={800} height={600} quality={75} />
}

These file sizes are illustrative. Results depend on the image, dimensions and quality. Check the browsers used by your audience rather than relying on fixed support percentages.

FormatExample at 1920 × 1080Support check
JPEG200 KBTarget browsers
WebP120 KBTarget browsers
AVIF80 KBTarget browsers

Common errors and a final comparison

IssueCheck
Missing valid TLS certificateConfigure a trusted production certificate, for example through Let’s Encrypt.
Brotli expected but not returnedInspect request and response headers, content type and proxy configuration.
Too many early resource requestsRemove unhelpful prefetch or preload hints and compare again.

Repeat tests with the same page, device, network and cache conditions. Speed Scanner and WebPageTest can help compare loading, while a protocol label alone cannot prove an improvement. Record the configuration and results, then recheck after relevant changes.

Website speed check

Find out what slows your website down

Check your page with Milten to see its Core Web Vitals and get recommendations for faster loading.

Check your website
Cookies

We use cookies

We use cookies to give you a better experience on our site. You can learn more about how we use cookies in our privacy policy.

HTTP/2 and Brotli in Next.js: setup and verification | Milten