Edge middleware[01]

Edge middleware

Decides, per request, who gets Markdown. Agents get the page's prebuilt .md; everyone else gets your page, untouched.

What it does

[02]
your edge · @ontosdk/next middleware
Someone asks yoursite.com for /pricing
  1. A known agent's user-agent, an Accept: text/markdown header, or ?onto. Anyone else goes straight through.

  2. Sent to Onto without waiting — only when a site key is set.

  3. The prebuilt /.onto/pricing.md, fetched from your own site. No file, and the page is served as usual.

  4. Serve Pro and above, when you've written some for the route. This step waits for Onto.

Markdown for agents, your page for everyone else

Try a request

[03]
GET /pricing
Your site
  1. Asking for Markdown — user-agent matches GPTBot (OpenAI)
  2. Note the visit — sent without waiting
  3. Fetch /.onto/pricing.md from your own site
  4. Add your context — asked, but Free gets nothing back
HTTP/1.1 200
Content-Type: text/markdown; charset=utf-8
Cache-Control: no-store, must-revalidate
Vary: User-Agent, Accept
X-Onto-Trace: GPTBot/1.2 (+https://openai.com/gptbot)
X-Onto-Bot: GPTBot (OpenAI)
X-Onto-Matched: true
# Pricing

> Plans and what each includes

**Source:** /pricing
…

Set it up

[04]

npx onto-next init writes this for you. If you already had a middleware.ts, it's left alone — call ontoMiddleware(req, ontoConfig) from yours.

middleware.ts
import { NextRequest } from 'next/server';
import { ontoMiddleware } from '@ontosdk/next/middleware';
import ontoConfig from './onto.config';

export const middleware = (req: NextRequest) => ontoMiddleware(req, ontoConfig);

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
};

Passing the config is what lets it answer /llms.txt itself. Paths under /_next and anything with a file extension are always passed through.

Who counts as an agent

[05]
Matches
Named crawlersGPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, Claude-User, PerplexityBot, Meta-ExternalAgent, Amazonbot, CCBot, Bytespider and more — 42 entries
Catch-allsGPT, Claude, OpenAI, Anthropic, Perplexity, Mistral — anywhere in the user-agent
HTTP librariescurl, Wget, axios, node-fetch, python-requests, httpx, Go-http-client
Left outGooglebot and Bingbot — search keeps getting your HTML

A match is any entry appearing in the user-agent, ignoring case; the longest match names the bot. Link-preview fetchers like facebookexternalhit are on the list too, so they receive Markdown.

What it reads

[06]
VariableUsed for
ONTO_API_KEYYour site key. Without it nothing is recorded and no context is added — Markdown is still served.
ONTO_API_URLWhere visits and context requests go. Defaults to https://api.buildonto.dev; ONTO_DASHBOARD_URL still works.