Guía · Next.js 1612 ago 2026 · 11 min

Next.js agent-ready: recipe mínima

La recipe mínima que no rompe el build de Docker y deja tu Next.js listo para agentes (n=1, 6 semanas, sin garantía de citas).

Recipe mínima — 4 pasos que no rompen el build

Basado en el caso real belmon.tech (6 semanas, 0 deps extra). Cubre AR-DISC-01/05 + AR-CONT-01/02 y los gotchas de Next 16/Turbopack que casi rompen el Docker build por leads_db.

1 · DB sin romper el build

// app/readiness/[lang]/page.tsx  (o cualquier page que lea DB)
import { connection } from "next/server";
import { Suspense } from "react";
import { getAllPlans } from "@/app/lib/plans";

export default async function Page() {
  await connection(); // difiere a request-time, clave en Docker sin leads_db
  return (
    <Suspense fallback={<p>Cargando planes…</p>}>
      <PlansList /> {/* dentro hace await getAllPlans() */}
    </Suspense>
  );
}
// Nunca uses export const dynamic = 'force-static' si lees searchParams o DB

2 · Link headers sin HTML

// next.config.ts
export const linkHeaders = async () => [
  {
    source: "/(.*)",
    headers: [
      { key: "Link", value: '</sitemap.xml>; rel="sitemap"' },
      { key: "Link", value: '</llms.txt>; rel="describedby"; type="text/plain"' },
      { key: "Link", value: '</.well-known/api-catalog>; rel="api-catalog"' },
    ],
  },
];

3 · Markdown for Agents

// proxy.ts  (Next 16 = proxy, no middleware)
export default function proxy(req: NextRequest) {
  if (req.headers.get("accept")?.includes("text/markdown")) {
    const url = req.nextUrl.clone();
    url.pathname = `/markdown${url.pathname}`;
    return NextResponse.rewrite(url); // sin query param
  }
}
// app/markdown/[[...path]]/route.ts  — importa de lib/*-data.ts, NUNCA de componentes con SCSS
// params es { path?: string[] } con [[...path]], o el type-check falla en Turbopack

4 · robots.txt que sí permite Content-Signal

// app/robots.txt/route.ts  — route handler, no MetadataRoute.Robots
export async function GET() {
  return new Response(
    `User-agent: GPTBot\nAllow: /\nUser-agent: ClaudeBot\nAllow: /\nContent-Signal: ai-train=no, search=yes, ai-input=yes\nSitemap: https://belmon.tech/sitemap.xml`,
    { headers: { "Content-Type": "text/plain" } }
  );
}
Gotchas Turbopack: Un rewrite con ?path= no llega al route handler — usa catch-all. El matcher del proxy debe excluir /_next, /api, /monitoring, /markdown y archivos con extensión. Y .well-known va como route handler en app/.well-known/..., no en public/ (Next ignora dotfiles).

Validación

curl -H "Accept: text/markdown" https://belmon.tech/readiness/es | head
curl https://belmon.tech/robots.txt
curl -I https://belmon.tech/ | grep -i link
POST https://isitagentready.com/api/scan  {"url":"https://belmon.tech"}
npx ora scan https://belmon.tech  # Deep Scan oficial de AgentReady
Limitaciones de esta recipe

n=1 sitio (belmon.tech), Next.js 16 + Turbopack, 6 semanas. No aplica tal cual a WordPress/CMS ni garantiza citas. Valida siempre con isitagentready antes de prometer resultados.


Glosario: llms.txt → · Implementación como servicio