External Publication
Visit Post

Your docs have dead links. I built a zero-dependency CLI that catches the local ones — no network

DEV Community [Unofficial] June 17, 2026
Source

You rename a file, restructure a folder, tweak a heading — and quietly leave a trail of dead links across your README and docs. Nobody notices until a reader clicks ./old-guide.md and gets a 404.

There are tools for this, but each asks for something:

  • markdown-link-check works, but it pulls in ~9 dependencies and its headline feature is hitting external URLs over HTTP — slow, rate-limited, and flaky in CI (your build shouldn't go red because someone's blog was down).
  • lychee is fast and great, but it's a Rust binary to install.
  • The old Python option, mlc, has been unmaintained since 2021.

I wanted the deterministic part — local links — to be instant and dependency-free. So I built linkbust :

npx linkbust



README.md:8      ✗  ./setup.md           (file not found)
docs/api.md:14   ✗  #usage               (no anchor in this file)
guide.md:3       ✗  ./api.md#missing     (no anchor in ./api.md)

✗ 3 broken · 142 local links checked · 38 external skipped (never fetched)

What it checks (and skips)

  • Relative file links and image sources resolve on disk.
  • Anchors — both [x](#section) in the same file and [x](other.md#section) across files — match a real heading (GitHub-style slug) or an explicit <a id="…">.
  • Reference links [text][ref] have a matching [ref]: … definition.
  • Links inside fenced/inline code blocks are ignored , so examples don't cause false alarms.

It deliberately never makes a network requesthttp(s), mailto:, and /absolute paths are classified and skipped. That's the whole design: because it's offline, it's instant and can't flake. Which makes it ideal as a pre-commit hook :

- repo: local
  hooks:
    - id: linkbust
      name: linkbust
      entry: npx linkbust
      language: system
      pass_filenames: false

Exit code is 1 if anything's broken, so CI fails cleanly.

Zero dependencies, both ecosystems

Pure standard library — a small Markdown parser (code masking, reference links, GitHub anchor slugs) plus filesystem checks. The Node and Python ports are behavior-identical, byte-for-byte (same output, same exit codes).

npx linkbust              # Node >= 18
pip install linkbust      # Python >= 3.8

Do you check links in CI, or only find out when someone reports a 404? And for external URLs — worth the flakiness to catch them, or do you treat those as out of scope too?

Discussion in the ATmosphere

Loading comments...