Codegea

Development

Custom software developmentSystems, platforms and integrations built around your operationCorporate websitesProfessional, fast and built to inspire trustOnline storesCatalog, payments and shipping ready to sell 24/7WooCommerceFast WooCommerce stores with each country’s payments and invoicingLanding pagesA page focused on one goal: turning visitors into customersMobile appsCustom iOS and Android apps that put your business in your customers’ pockets

Integration and CMS

CMS integrationsWordPress, Strapi, Directus and Arc XP connected to your whole ecosystemArc XPSupport, implementation and integrations for the media platformMigrationsMigrate your site, platform or database without losing anything

Growth and infrastructure

AI automationTechnology does the repetitive work and your team focuses on what mattersOn-page SEOBe found and recommended by Google: technical and content optimizationServers & hostingFast, secure servers monitored 24/7
RetainerYour dev team, by the monthFrom 20h a month. Fixed fee.
An AI quoting tool to quote services in minutesProfessional quotes in minutes, with projects, sellers and payments tracked in one place.See the case

By type

EcommerceStores and catalogsCorporate sitesBrand and lead genPlatforms & appsCustom software

Explore

All projectsCases and resultsClientsWho trusts usRetainerMonthly dev team

Blog

All articlesGuides and opinionNewsletterOne email a monthContactWe reply in 24h

Latest articles

How codegea.com is built: Next.js, Payload, Turso and R2CMS · 2026-10-20Cloudflare Workers: what they are and when to use themNode.js · 2026-10-19Live Preview in Payload with Next.jsCMS · 2026-10-19
NewsletterJoin our newsletterPractical web, SEO and AI notes.
ServicesCustom software developmentCorporate websitesOnline storesWooCommerceLanding pagesMobile appsCMS integrationsArc XPMigrationsAI automationOn-page SEOServers & hostingYour dev team, by the month
ProjectsProfessional quotes in minutes, with projects, sellers and payments tracked in one place.EcommerceCorporate sitesPlatforms & appsAll projectsClientsRetainer
Retainer
BlogAll articlesNewsletterContactHow codegea.com is built: Next.js, Payload, Turso and R2Cloudflare Workers: what they are and when to use themLive Preview in Payload with Next.jsJoin our newsletter
ESEN
Let's talk

Web

How to serve your pages as Markdown for AI agents

Add .md to an article's URL and get the clean text. How we did it in Next.js with a proxy, a route and a Lexical to Markdown conversion.

Derian Pinto · Founder & Full Stack Developer2026-10-192 min

An AI assistant that reads your website gets HTML: menus, scripts, footer, a lot of noise around the text it cares about. A Markdown version of each article gives it only what matters: the title, the summary and the content. On this site, adding .md to the URL of an article or project returns it. This is how it is done.

If you want to see the result, try it with this very article: /en/serve-your-pages-as-markdown-for-ai-agents.md.

The three pieces

  1. A rewrite in the proxy: any URL ending in .md is sent to an internal route.
  2. A route handler that finds the content and answers with text/markdown.
  3. A function that converts the content to Markdown.

Piece 1: the proxy

In proxy.ts, before the language logic:

// Markdown version for agents: /<article>.md and /projects/<slug>.md
if (pathname.endsWith('.md')) {
  const name = rest.at(-1)?.slice(0, -3);
  const section =
    rest.length === 1 ? 'blog' : rest.length === 2 ? matchSegment(locale, rest[0])?.internal : undefined;
  if (!name || (section !== 'blog' && section !== 'projects')) return NextResponse.next();
  return NextResponse.rewrite(new URL(join('md', locale, section, name), req.url));
}

Articles live at the root (/my-article), so a URL with a single segment is an article (blog). With two segments, the first indicates the section, for example projects. Whatever doesn't fit goes on its normal way.

Piece 2: the route handler

In app/md/[locale]/[section]/[slug]/route.ts:

const markdown = (body: string) =>
  new Response(body, {
    headers: {
      'Content-Type': 'text/markdown; charset=utf-8',
      'X-Robots-Tag': 'noindex',
      'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
    },
  });

export async function GET(_req: NextRequest, { params }: Ctx) {
  const { locale, section, slug } = await params;
  if (!isLocale(locale)) return new Response('Not found', { status: 404 });

  if (section === 'blog') {
    const post = await getPost({ slug, locale });
    return post ? markdown(postMarkdown(post, locale)) : new Response('Not found', { status: 404 });
  }
  // …the same for projects
}

Two decisions:

  • X-Robots-Tag: noindex. The Markdown version shouldn't compete with the HTML page on Google. It is for machines, not for the search engine.
  • One-hour public cache. The text rarely changes, and an agent requesting many pages shouldn't hit the database every time.

Piece 3: from Lexical to Markdown

Our articles' content lives in Payload, which uses the Lexical editor and stores it as a JSON tree. You have to walk it and write Markdown. Our postMarkdown function builds the document:

export function postMarkdown(post: Post, locale: Locale): string {
  return join([
    `# ${post.title}`,
    `> ${post.excerpt}`,
    [
      `URL: ${SITE_URL}${postPath(locale, post.slug)}`,
      `${category.title} · ${post.publishedAt?.slice(0, 10)} · ${author.name}`,
    ].join('\n'),
    lexicalToMarkdown(post.content.root),
  ]);
}

Title, summary as a quote, a line with the original URL, the category, date and author, and then the body. The original URL lets whoever reads the Markdown version know where the canonical one is.

lexicalToMarkdown walks the nodes and returns headings, quotes, lists, tables, images, links and code blocks with their language. If your content is in Markdown from the start (for example, a Markdown-based CMS), you skip this piece and return the file.

A complement: llms.txt

Besides the .md versions, we serve llms.txt and llms-full.txt at the root: text indexes with the site description and links to services, projects and articles. It is a recent convention, and there is no guarantee every agent reads it. It costs little to maintain, because it is generated from the same database.

What it doesn't do

This doesn't guarantee any model will cite you or improve your position on Google. What it does is make reading easier for whoever decides to read you, and it is a small job: a proxy, a route and a converter.

If you are preparing a site to be read by machines, a technical SEO audit in 30 minutes will also help.

Share this article?LinkedInWhatsAppX

Written by

Derian Pinto

Founder & Full Stack Developer

Founder of Codegea. Builds custom websites, stores and platforms, and integrates and supports Arc XP for media companies.

Join our newsletter

An email with news and practical web, SEO and AI notes. No spam, unsubscribe anytime.

Shall we build your project?

Need help? Chat with us