MD051 - Link anchors should exist¶
Aliases: link-fragments
What this rule does¶
Ensures that links to sections within the same document (like #introduction) point to actual headings that exist. Cross-file fragment links (like file.md#heading) are not validated by this rule.
Why this matters¶
- Navigation: Broken internal links frustrate readers trying to jump to sections
- Maintenance: Helps you catch links that break when headings are renamed
- User experience: Ensures smooth document navigation
Examples¶
✅ Correct¶
# Introduction
## Getting Started
See the [Introduction](#introduction) for background.
Jump to [Getting Started](#getting-started) to begin.
# Working with **bold** and *italic*
Link to [formatted heading](#working-with-bold-and-italic)
<!-- Cross-file links are ignored by MD051 -->
See [external documentation](README.md#setup) for setup instructions.
❌ Incorrect¶
# Introduction
## Getting Started
[Jump to Installation](#installation) <!-- No "Installation" heading exists -->
[See Overview](#overview) <!-- No "Overview" heading exists -->
🔧 Fixed¶
This rule cannot automatically fix missing anchors - you need to either:
- Add the missing heading
- Update the link to point to an existing heading
- Remove the broken link
Configuration¶
This rule supports configuring the anchor generation style to match different Markdown processors:
[MD051]
# Anchor generation style (default: "github")
# - "github": Preserves Unicode, underscores, and consecutive hyphens
# - "kramdown": ASCII-only with normalization, removes underscores
# - "bitbucket": Adds 'markdown-header-' prefix
anchor-style = "github"
Anchor style differences¶
| Heading | GitHub | kramdown | Pandoc | Bitbucket |
|---|---|---|---|---|
Hello World |
#hello-world |
#hello-world |
#hello-world |
#markdown-header-hello-world |
respect_gitignore |
#respect_gitignore |
#respectgitignore |
#respect_gitignore |
#markdown-header-respect_gitignore |
snake_case_example |
#snake_case_example |
#snakecaseexample |
#snake_case_example |
#markdown-header-snake_case_example |
123 Numbers |
#123-numbers |
#numbers |
#numbers |
#markdown-header-123-numbers |
_underscore |
#_underscore |
#underscore |
#underscore |
#markdown-header-_underscore |
The End - yay |
#the-end---yay |
#the-end---yay |
#the-end---yay |
#markdown-header-the-end-yay |
CI/CD Migration |
#cicd-migration |
#cicd-migration |
#cicd-migration |
#markdown-header-cicd-migration |
Café au Lait |
#café-au-lait |
#cafe-au-lait |
#café-au-lait |
#markdown-header-cafe-au-lait |
Über uns |
#über-uns |
#uber-uns |
#über-uns |
#markdown-header-uber-uns |
你好世界 |
#你好世界 |
#section |
#你好世界 |
#markdown-header- |
こんにちは |
#こんにちは |
#section |
#こんにちは |
#markdown-header- |
Key differences:
- GitHub: Preserves Unicode characters (Chinese, Japanese, Arabic, accented Latin), underscores, and consecutive hyphens
- kramdown: Normalizes accented Latin to ASCII, converts non-Latin scripts to
#section, removes underscores, preserves consecutive hyphens - Bitbucket: Adds
markdown-header-prefix, normalizes accented characters, preserves underscores, collapses consecutive hyphens
Automatic fixes¶
This rule does not provide automatic fixes since it cannot guess which heading you meant to link to.
How heading anchors work¶
When you create a heading like ## Getting Started, Markdown automatically creates an anchor #getting-started that you can link to. The conversion follows these rules:
- Convert to lowercase:
Getting Started→getting started - Replace spaces with hyphens:
getting started→getting-started - Remove special characters:
FAQ's & Tips!→faqs-tips - Strip formatting:
**Bold** Text→bold-text
Learn more¶
- CommonMark anchors - How link anchors work
- GitHub heading IDs - GitHub's approach to heading anchors