llms.txt for GitHub Pages
Three approaches for GitHub Pages: a static file committed to docs/ or gh-pages, a Jekyll template that auto-generates content, or a GitHub Actions workflow for fully automated generation.
Last updated:
Option 1: static file in docs/ or gh-pages
The simplest approach is to commit llms.txt as a plain file to your repository. GitHub
Pages serves it as-is with the correct MIME type.
# GitHub Pages, static file approach
#
# Serving from docs/ branch (most common):
# your-repo/
# ├── docs/
# │ ├── index.html
# │ └── llms.txt ← add this file here
# └── README.md
#
# Serving from gh-pages branch:
# Create or switch to the gh-pages branch, then add:
# llms.txt ← in the branch root
#
# In repository Settings → Pages:
# Source: Deploy from a branch
# Branch: main (or gh-pages), Folder: /docs (or /)
#
# GitHub Pages serves it at: https://username.github.io/repo/llms.txt
# With a custom domain: https://yourdomain.com/llms.txt
GitHub Pages automatically detects plain-text files and serves them with Content-Type: text/plain; charset=utf-8. No configuration needed.
Option 2: Jekyll with layout: null
If your GitHub Pages site uses Jekyll (the default for many repos), a plain .txt file will pass through without modification, no front matter needed. However, if
you want Jekyll to process the file as a template (e.g. to inject site variables), add a front matter
block with layout: null to prevent Jekyll from wrapping it in an HTML layout.
---
layout: null
permalink: /llms.txt
---
# My Project
> One-sentence description of what my project does.
## Documentation
- [Getting Started](https://myproject.com/docs/start/): Install and configure.
- [API Reference](https://myproject.com/docs/api/): Full endpoint catalog.
## Optional
- [Changelog](https://myproject.com/changelog/): Release history.
You can also drive the link list from a YAML data file in _data/, keeping the content separate from the template:
# _data/llms_links.yml
docs:
- title: "Getting Started"
url: "https://myproject.com/docs/start/"
desc: "Install and configure in minutes."
- title: "API Reference"
url: "https://myproject.com/docs/api/"
desc: "Full endpoint catalog with examples."
optional:
- title: "Changelog"
url: "https://myproject.com/changelog/"
desc: "Release history."
---
layout: null
permalink: /llms.txt
---
# {{ site.title }}
> {{ site.description }}
## Documentation
{% for link in site.data.llms_links.docs %}
- [{{ link.title }}]({{ link.url }}): {{ link.desc }}
{% endfor %}
## Optional
{% for link in site.data.llms_links.optional %}
- [{{ link.title }}]({{ link.url }}): {{ link.desc }}
{% endfor %}
Option 3: GitHub Actions for dynamic generation
For sites where the link list should be auto-generated from your content (Markdown files, frontmatter, a CMS), use a GitHub Actions workflow that runs a script and commits the result back to the repository.
# .github/workflows/generate-llms-txt.yml
# Generates llms.txt from your content and commits it to the repo.
name: Generate llms.txt
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'content/**'
workflow_dispatch:
jobs:
generate:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Generate llms.txt
run: node scripts/generate-llms-txt.js
- name: Commit and push if changed
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add docs/llms.txt
git diff --staged --quiet || git commit -m "chore: regenerate llms.txt"
git push
// scripts/generate-llms-txt.js
// Run by GitHub Actions to generate docs/llms.txt from your content
const fs = require('fs');
const path = require('path');
const SITE_URL = 'https://myproject.com';
// Example: build link list from your markdown files in docs/
const docsDir = path.join(__dirname, '..', 'docs');
const mdFiles = fs.readdirSync(docsDir)
.filter(f => f.endsWith('.md') && f !== 'index.md');
const links = mdFiles.map(file => {
const content = fs.readFileSync(path.join(docsDir, file), 'utf-8');
const titleMatch = content.match(/^#\s+(.+)/m);
const descMatch = content.match(/^>\s+(.+)/m);
const slug = file.replace('.md', '');
const title = titleMatch ? titleMatch[1] : slug;
const desc = descMatch ? descMatch[1] : '';
return `- [${title}](${SITE_URL}/docs/${slug}/): ${desc}`;
}).join('\n');
const output = [
'# My Project',
'',
'> My project description.',
'',
'## Documentation',
'',
links,
'',
'## Optional',
'',
`- [Changelog](${SITE_URL}/changelog/): Release history.`,
].join('\n');
fs.writeFileSync(path.join(docsDir, 'llms.txt'), output, 'utf-8');
console.log('Generated docs/llms.txt');
The workflow triggers on pushes to main that modify your content directories, and can
also be run manually via workflow_dispatch. It only creates a commit if the
generated file actually changed.
Custom domain and CNAME
If you use a custom domain (e.g. myproject.com), add a CNAME file to your docs directory containing your domain. GitHub Pages will serve your
site, including llms.txt, at that domain.
myproject.com
Use absolute URLs in your llms.txt that match your custom domain, not the default username.github.io/repo URL.
Verify
curl -I https://myproject.com/llms.txt
# Expected:
# HTTP/2 200
# content-type: text/plain; charset=utf-8
curl https://myproject.com/llms.txt | head -5 Related guides
- How to create llms.txt, templates and checklist.
- llms.txt format reference, spec details.
- Eleventy guide, another static site generator.
- Hugo guide, static site with Go templates.
- Validator · Generator.