afaqrashid.com  ·  portfolio · 2026 ed.

Next.js generateMetadata Fetching Twice? What I Tested

A dynamic page usually loads the same item twice: once in generateMetadata for the title, and once in the page to render it. Whether that is one request or two depends on how you load it. A GET fetch() ran once. A POST fetch() and a non-fetch call ran twice, until I wrapped the call in React's cache().

I tested this on Next.js 16.4.4 with next build and next start. Each page called the same function in generateMetadata and in the page component. The function hit a small local server that counted every request it received, so the numbers below are real requests, not log lines.

The results

Data call                            Requests per page load
fetch(url)  (GET)                    1
fetch(url, { method: "POST" })       2
Non-fetch call, no cache()           2
Non-fetch call wrapped in cache()    1

The counts were the same with a Chrome user agent and with Googlebot's.

GET fetch: deduplicated for you

// lib/items.ts
export async function getItem(slug: string) {
  const res = await fetch(`https://api.example.com/items/${slug}`);
  return res.json();
}

// app/items/[slug]/page.tsx
export async function generateMetadata({ params }: Props) {
  const item = await getItem((await params).slug);
  return { title: item.title };
}

export default async function Page({ params }: Props) {
  const item = await getItem((await params).slug);
  return <h1>{item.title}</h1>;
}

One request reached the server per page load. The generateMetadata docs say fetch requests are memoized across generateMetadata, generateStaticParams, layouts, pages and Server Components.

POST and database calls run twice

Changing only the method to POST doubled the requests. Many GraphQL clients and some CMS SDKs send POST requests, so they fall into this case.

A database client or ORM doesn't go through fetch at all. For that case I used a function that is not fetch from Next.js's point of view, and it also ran twice. That is what the docs warn about: if you're not using fetch, deduplicate with React's cache function.

The fix: wrap the loader in cache()

// lib/items.ts
import { cache } from "react";

export const getItem = cache(async (slug: string) => {
  return db.item.findUnique({ where: { slug } }); // or a POST / GraphQL call
});

Call getItem from generateMetadata, the page and any layout, as before. With cache(), my counter went from 2 requests to 1. Define it once at module level and import it everywhere, so every caller shares the same cached function.

It is per request, not a cache

Both versions are deduplication within one page load. I loaded the same page twice in a row: the GET fetch reached the server 2 times, and so did the cache() version. Each visit still loads fresh data.

If you want data kept between visits, that is a separate choice: a fetch cache option, revalidate, or Cache Components, depending on your setup. Don't expect cache() from React to do it.

Why this matters beyond speed

If your data source is rate-limited or billed per request, the duplicate call doubles the cost of every page view, and every crawler visit counts as one.

Check your own pages

Add a log line inside your loader, run next build and next start, and load a page once:

export const getItem = cache(async (slug: string) => {
  console.log("getItem", slug);
  // ...
});

One line per page load means it is deduplicated. Two lines mean generateMetadata and the page each made their own call. Measure with next build and next start, as I did, not next dev.

Related

Sources