A Markdown link can point to a heading with a fragment such as #installation, but Markdown itself does not define one universal algorithm for generating heading IDs. GitHub, documentation generators, editor previews, and CommonMark-based viewers can turn the same heading into different anchors—especially when punctuation, duplicate headings, or non-ASCII text is involved.
This guide focuses on cross-renderer heading links. For ordinary URLs, images, reference links, and repository paths, use the broader Markdown link syntax guide.
For a simple heading, lowercase the visible text and replace spaces with hyphens:
[Go to installation](#installation)
## Installation
This pattern works across common renderers because the heading contains only plain letters. Problems begin when the generated ID requires decisions that platforms make differently.
Consider this heading:
## What's New in v2.0?
A likely fragment is #whats-new-in-v20, but punctuation-removal rules are renderer behavior rather than a guarantee from core Markdown. Apostrophes, periods, parentheses, slashes, emoji, and symbols may be removed, retained, encoded, or normalized differently.
For durable documentation, make headings that serve as link targets concise and punctuation-light:
## Version 2 changes
Then link with:
[Version 2 changes](#version-2-changes)
The prose under the heading can carry the more expressive wording.
Repeated headings require unique generated IDs:
## Examples
## Configuration
## Examples
Many renderers append a numeric suffix to the second occurrence, but the suffix and starting number can vary. A guessed link such as #examples-1 may work on one platform and fail on another.
Prefer unique headings:
## Flowchart examples
## Configuration
## XY chart examples
Unique visible text improves scanning and avoids coupling links to a renderer's duplicate-ID counter.
Heading IDs made from accented letters, Cyrillic, Japanese, Korean, or other scripts may be preserved, normalized, percent-encoded in copied URLs, or transliterated by a publishing system. A fragment copied from the rendered page is safer than one typed from memory.
If a document must work across several renderers, you have three practical choices:
Do not assume raw HTML IDs are universally permitted. Some Markdown hosts sanitize or remove HTML.
Combine the relative path with the heading fragment:
[Authentication errors](./api.md#authentication-errors)
Two independent pieces must be correct: ./api.md must resolve from the current file, and #authentication-errors must match the ID generated when that destination file is rendered.
Repository websites sometimes rewrite .md paths into web routes. Use repository-relative links for source portability, then test them on the host where readers will click them. The detailed local links and images guide explains how path resolution changes when files are moved or uploaded alone.
CommonMark defines how a link destination is parsed, but it does not require a specific heading-ID algorithm. A renderer can follow CommonMark for the link itself and still use its own rules to generate the destination heading's fragment.
That distinction explains a common failure:
[API reference](#api-reference) is valid Markdown link syntax.id="api-reference".Changing brackets or parentheses cannot repair an ID mismatch. Inspect the rendered heading or copy its link from the destination platform.
When a same-page heading link fails, check in this order:
For another-file links, check the file path before the fragment. A correct anchor cannot fix a destination file that was not published.
When heading links are important, keep a small section in the real document that exercises its risky cases:
- [API v2](#api-v2)
- [Cafe setup](#cafe-setup)
- [Windows and Linux](#windows-and-linux)
## API v2
## Cafe setup
## Windows and Linux
Use production wording rather than inventing punctuation-heavy test headings. Open the file in each supported destination and click the links. A static syntax check can confirm the fragment exists in source, but only the renderer determines the generated ID.
The most portable link targets share a few traits:
Changing a heading can break inbound links from other documents and search results. Treat important headings like small public URLs: rename them deliberately and update references together.
Open the Markdown in mdview.io to click through its rendered heading links. If GitHub or another platform is the final destination, repeat the check there because heading-ID behavior belongs to that renderer.
No. CommonMark defines headings and links, but generated HTML IDs and fragment conventions are extensions chosen by renderers.
#examples-1 sometimes fail?It usually targets a duplicate heading, and platforms do not all use the same duplicate suffix convention. Unique heading text is more portable.
Only when every destination permits and preserves that HTML or attribute syntax. Hosted platforms may sanitize it, so simple unique headings are the safer baseline.