Markdown Link Checker

Check anchors, duplicate headings, empty link text, and image alt text.

5
links
5
anchors
3
issues

3 in-page links resolved against this document, 1 reference definition. External URLs are never requested.

3 link issues found.

  • Line 7Broken anchor
    No heading matches #config.
    #config
    Did you mean#configuration
  • Line 15Image alt text
    Image needs descriptive alt text.
    ./usage.png
  • Line 19Empty link text
    Link text is empty.
    https://example.com/env

Anchors in this document

  • Widget SDK
    #widget-sdk
  • Contents
    #contents
  • Installation
    #installation
  • Usage
    #usage
  • Configuration
    #configuration

How to Use

  1. Paste a README, docs page, or any Markdown file into the left pane
  2. Read the counts at the top right — total links, generated anchors, and open issues
  3. Work through the issue list; each entry gives the line number and the offending target
  4. Copy the correct slug from the anchor list, or from the suggestion on a broken link
  5. Fix the source in your editor and paste again to confirm the list is empty

What Gets Checked

Broken in-page links are the most common defect in long READMEs, and they are invisible until someone clicks. A table of contents written by hand drifts as soon as a heading is renamed, and nothing in a normal Markdown build fails when it does. This checker rebuilds every heading anchor the way GitHub would and compares your #fragment links against that list. The whole document is read first, so a contents list at the top resolves against the sections below it.

  • Missing anchors — a [link](#section) with no heading that generates the slug section, with the nearest real anchor offered when there is one
  • Duplicate anchors — two headings producing the same slug, where GitHub silently appends -1 to the second one
  • Undefined references[text][label] with no [label]: url anywhere in the file, which GitHub renders as literal brackets rather than a link
  • Empty link text[](https://example.com), which renders as an unclickable gap and tells assistive tech nothing
  • Weak image alt text — an empty alt, or a placeholder such as image or screenshot, including badges wrapped in a link

How GitHub builds a heading anchor

GitHub lowercases the heading text, drops punctuation, and replaces each space with a hyphen. So ## Getting Started! becomes #getting-started. Two details trip up most checkers: underscores are kept, so ## get_user_name is #get_user_name, and the slug comes from the text GitHub renders, so ## Install `cli` becomes #install-cli and ## [Docs](https://example.com) becomes #docs. Fenced code, inline code, HTML comments, and YAML front matter are skipped. This is a structural audit only — no external URL is ever requested, so nothing leaves your browser.

Frequently Asked Questions

Does it crawl external URLs?

No. This tool checks Markdown structure locally: heading anchors, duplicate headings, undefined link references, link text, and image alt text. It never makes network requests, so it cannot tell you whether a remote page returns 404.

Which anchor style is used?

GitHub-style heading anchors. Headings are lowercased, punctuation is removed, and spaces become hyphens — the same slug GitHub generates for README and documentation links. Underscores survive, so an API reference heading like get_user_name keeps its anchor, and a heading that contains a link or a code span is slugged from the text GitHub renders rather than from its Markdown source.

Does a table of contents at the top of the file confuse it?

No. Every heading, HTML anchor, and link reference definition in the document is collected before a single link is checked, so a table of contents written above the sections it points at resolves normally. Definitions parked at the bottom of the file resolve the same way.

Why is a duplicate heading reported as a problem?

Two headings with the same text produce the same anchor slug, so GitHub suffixes the later ones with -1, -2, and so on. Any link you wrote to the plain slug then lands on the first heading instead of the one you meant. The suffixed anchor is real, so a link to #usage-1 is accepted.

What counts as missing image alt text?

An empty alt, plus placeholder values that carry no information: image, screenshot, photo, alt text, and missing alt. Screen readers announce those verbatim, so they are treated as unwritten. Badges nested inside a link are checked too, since that is where most unlabelled README images live.

Are code blocks checked?

No. Fenced code blocks, inline code spans, HTML comments, and YAML front matter are all skipped, so example Markdown inside backticks is never reported as a real link or heading in your document.

Is my document uploaded?

No. The whole audit runs in your browser and your Markdown is autosaved only to this browser local storage.

Related Tools