How to Block AI Bots on VitePress: Complete 2026 Guide
VitePress is the documentation engine behind Vue, Vite, Rollup, Vitest, Pinia, and dozens of other major open-source projects. Built on Vite and Vue 3, it generates a static site with SSG. Bot blocking splits across the content layer (VitePress config and public/) and the hosting platform layer.
Contents
robots.txt in public/
VitePress copies everything in the public/ directory (at your project root, not inside docs/) to the root of the build output at .vitepress/dist/. Place robots.txt in public/and it will be served at https://yoursite.com/robots.txt — no config required.
public/ directory must be at the same level as your .vitepress/ config directory — typically the project root or your docs/ directory, depending on how your project is structured. If your config is at docs/.vitepress/config.ts, put public/robots.txt at docs/public/robots.txt.public/robots.txt
User-agent: *
Allow: /
# Block AI training crawlers
User-agent: GPTBot
Disallow: /
User-agent: ClaudeBot
Disallow: /
User-agent: anthropic-ai
Disallow: /
User-agent: CCBot
Disallow: /
User-agent: Google-Extended
Disallow: /
User-agent: AhrefsBot
Disallow: /
User-agent: Bytespider
Disallow: /
User-agent: Amazonbot
Disallow: /
User-agent: Diffbot
Disallow: /
User-agent: FacebookBot
Disallow: /
User-agent: cohere-ai
Disallow: /
User-agent: PerplexityBot
Disallow: /
User-agent: YouBot
Disallow: /
Sitemap: https://docs.example.com/sitemap.xmlAfter npx vitepress build, verify: ls .vitepress/dist/robots.txt.
noai meta via transformHead hook
VitePress provides a transformHead build hook in .vitepress/config.ts. It runs for every page during static generation and lets you inject <head> entries programmatically — including the noai meta tag.
.vitepress/config.ts
import { defineConfig } from 'vitepress'
import type { TransformContext } from 'vitepress'
export default defineConfig({
title: 'My Docs',
description: 'My documentation site',
// Global head entries — applied to every page
head: [
// Fallback: set here if not using transformHead
// ['meta', { name: 'robots', content: 'noai, noimageai' }],
],
// Per-page head injection — runs at build time for every page
async transformHead({ pageData }: TransformContext) {
const robots: string =
(pageData.frontmatter.robots as string | undefined) ?? 'noai, noimageai'
return [
['meta', { name: 'robots', content: robots }],
]
},
})headarray are injected globally into every page — no per-page logic possible. The transformHead hook runs per page, giving you access to pageData.frontmatter for conditional logic. Use transformHeadwhen you need per-page overrides; use head for truly global static tags.Simple approach — global head array (no per-page override)
If you don't need per-page overrides, the simpler approach is the global head array:
import { defineConfig } from 'vitepress'
export default defineConfig({
title: 'My Docs',
head: [
['meta', { name: 'robots', content: 'noai, noimageai' }],
],
})Per-page override via frontmatter
With the transformHead hook reading pageData.frontmatter.robots, override the meta tag on any individual page via its YAML frontmatter:
Default (no frontmatter — uses hook default)
---
title: My Page
---
# My Page
Page content here.Allow indexing but no AI training
---
title: My Public Page
robots: "index, follow, noai, noimageai"
---Allow everything (e.g. landing / home page)
---
title: Home
robots: "index, follow"
---Block everything
---
title: Internal Page
robots: "noindex, noai, noimageai"
---Using the head frontmatter key directly
VitePress also supports a head array in frontmatter that merges with the global head config — useful for one-off overrides without touching transformHead:
---
title: My Page
head:
- - meta
- name: robots
content: "index, follow"
---head entries are merged with global head entries — they do not replace them. If you set a global robots meta in head and also set one via frontmatter, both will appear in the output. Use transformHead for clean per-page override (it controls the logic yourself).X-Robots-Tag via hosting platform
X-Robots-Tag is an HTTP response header — VitePress outputs static files. Add it at the hosting layer.
Netlify — netlify.toml
[build]
command = "npx vitepress build docs"
publish = "docs/.vitepress/dist"
[[headers]]
for = "/*"
[headers.values]
X-Robots-Tag = "noai, noimageai"Vercel — vercel.json
{
"buildCommand": "npx vitepress build docs",
"outputDirectory": "docs/.vitepress/dist",
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Robots-Tag",
"value": "noai, noimageai"
}
]
}
]
}Cloudflare Pages — docs/public/_headers
VitePress copies public/ to .vitepress/dist/. Place _headers in docs/public/_headers (or public/_headers at the project root if that's your structure):
/*
X-Robots-Tag: noai, noimageaiGitHub Pages
noai meta tag via transformHead or global head is your only option. For X-Robots-Tag or hard 403 blocking, migrate to Netlify, Vercel, or Cloudflare Pages.Hard 403 via edge functions
Netlify Edge Function
Create netlify/edge-functions/block-ai-bots.ts:
import type { Context } from '@netlify/edge-functions';
const AI_BOTS = [
'GPTBot', 'ClaudeBot', 'anthropic-ai', 'CCBot',
'Google-Extended', 'AhrefsBot', 'Bytespider',
'Amazonbot', 'Diffbot', 'FacebookBot', 'cohere-ai',
'PerplexityBot', 'YouBot',
];
export default async function handler(
request: Request,
context: Context
): Promise<Response> {
const ua = request.headers.get('user-agent') || '';
const isBot = AI_BOTS.some((bot) =>
ua.toLowerCase().includes(bot.toLowerCase())
);
if (isBot) {
return new Response('Forbidden', { status: 403 });
}
return context.next();
}
export const config = { path: '/*' };Register in netlify.toml:
[[edge_functions]]
path = "/*"
function = "block-ai-bots"Cloudflare Pages Functions
Create functions/_middleware.ts at the project root:
import type { PagesFunction } from '@cloudflare/workers-types';
const AI_BOTS = [
'GPTBot', 'ClaudeBot', 'anthropic-ai', 'CCBot',
'Google-Extended', 'AhrefsBot', 'Bytespider',
'Amazonbot', 'Diffbot', 'FacebookBot', 'cohere-ai',
'PerplexityBot', 'YouBot',
];
export const onRequest: PagesFunction = async (context) => {
const ua = context.request.headers.get('user-agent') || '';
const isBot = AI_BOTS.some((bot) =>
ua.toLowerCase().includes(bot.toLowerCase())
);
if (isBot) {
return new Response('Forbidden', { status: 403 });
}
return context.next();
};Deployment quick-reference
| Platform | Build command | Publish dir | Custom headers | Edge functions |
|---|---|---|---|---|
| Netlify | npx vitepress build docs | docs/.vitepress/dist | ✅ netlify.toml | ✅ netlify/edge-functions/ |
| Vercel | npx vitepress build docs | docs/.vitepress/dist | ✅ vercel.json | ⚠️ Next.js required |
| Cloudflare Pages | npx vitepress build docs | docs/.vitepress/dist | ✅ public/_headers | ✅ functions/_middleware.ts |
| GitHub Pages | CI: npx vitepress build | .vitepress/dist | 🚫 No | 🚫 No |
| AWS Amplify | npx vitepress build docs | docs/.vitepress/dist | ✅ amplify.yml customHeaders | ✅ Lambda@Edge |
Full .vitepress/config.ts
import { defineConfig } from 'vitepress'
import type { TransformContext } from 'vitepress'
export default defineConfig({
title: 'My Docs',
description: 'Documentation site',
// noai for all pages via transformHead (enables per-page frontmatter override)
async transformHead({ pageData }: TransformContext) {
const robots: string =
(pageData.frontmatter.robots as string | undefined) ?? 'noai, noimageai'
return [['meta', { name: 'robots', content: robots }]]
},
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' },
],
sidebar: {
'/guide/': [
{ text: 'Introduction', link: '/guide/' },
],
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/your-org/your-repo' },
],
},
})FAQ
How do I add robots.txt to a VitePress site?
Place robots.txt in the public/ directory. VitePress copies everything in public/ to the root of .vitepress/dist/ on build — no configuration required. If your docs are in a docs/subdirectory, put it at docs/public/robots.txt.
How do I add the noai meta tag to every VitePress page?
Use the transformHead hook in .vitepress/config.ts. Return [["meta", { name: "robots", content: "noai, noimageai" }]]. For per-page override, read pageData.frontmatter.robots with a default fallback.
How do I override the robots meta tag on a specific VitePress page?
Add robots: "index, follow" to the page's YAML frontmatter. The transformHead hook reads pageData.frontmatter.robots ?? 'noai, noimageai' — the frontmatter value overrides the default.
What is the transformHead hook in VitePress?
A build-time hook in .vitepress/config.ts that runs for every page during static generation. It receives a TransformContext with pageData (frontmatter, filePath) and returns an array of HeadConfig tuples ([tag, attrs] or [tag, attrs, content]) to inject into that page's <head>.
How do I add X-Robots-Tag on a VitePress site?
At the hosting layer. Netlify: [[headers]] in netlify.toml. Vercel: headers() in vercel.json. Cloudflare Pages: _headers in public/ (copied to dist root by VitePress).
Does VitePress have a built-in way to block AI bots?
No built-in feature — use public/robots.txt and the transformHead hook for the content layer. For hard blocking, use a Netlify Edge Function or Cloudflare Pages middleware.
Is your site protected from AI bots?
Run a free scan to check your robots.txt, meta tags, and overall AI readiness score.