Skip to content

srmdn/go-linkchecker

go-linkchecker

A fast, zero-dependency link checker for markdown-based content today. It scans .md files recursively, checks every HTTP/HTTPS URL, and reports broken links. Built for self-hosted blogs and static sites, with a roadmap toward broader content sources.

Live landing page: https://srmdn.github.io/go-linkchecker/

Features

  • Scans all .md files in a directory recursively
  • Concurrent HTTP checks (configurable)
  • HEAD → GET fallback - handles sites that block HEAD requests
  • Global URL deduplication - same URL across multiple files checked once
  • Three-section report: Broken, OK, and Skipped
  • Optional multipart email delivery via SMTPS (plain-text + styled HTML)
  • Skip URLs by regex pattern or ignore specific HTTP status codes (useful for bot-hostile or trusted domains)
  • CI-friendly: exits with code 1 if broken links found
  • Zero external dependencies - standard library only

Works with

Supported today: any stack that stores content as .md files on disk - Hugo, Jekyll, Astro, Eleventy, VitePress, and similar static site generators.

Not supported yet: WordPress, database-backed CMS, MDX, HTML files, or live site crawling. See issue #2 for the roadmap.

Roadmap

  • Planned: MDX and HTML support via broader file extension scanning
  • Planned: sitemap input for live sites
  • Planned: URL-list input for CMS-backed or exported content

Install

Prebuilt binaries for Linux, macOS, and Windows are published on the GitHub Releases page. You do not need Go installed if you use a release binary.

Download A Release Binary

Replace VERSION below with the release you want, such as 0.5.2.

Linux (amd64):

VERSION=0.5.2
curl -LO "https://github.com/srmdn/go-linkchecker/releases/download/v${VERSION}/go-linkchecker_${VERSION}_linux_amd64.tar.gz"
tar -xzf "go-linkchecker_${VERSION}_linux_amd64.tar.gz"
./go-linkchecker --version

macOS (arm64):

VERSION=0.5.2
curl -LO "https://github.com/srmdn/go-linkchecker/releases/download/v${VERSION}/go-linkchecker_${VERSION}_darwin_arm64.tar.gz"
tar -xzf "go-linkchecker_${VERSION}_darwin_arm64.tar.gz"
./go-linkchecker --version

Windows (amd64, PowerShell):

$Version = "0.5.2"
curl.exe -LO "https://github.com/srmdn/go-linkchecker/releases/download/v$Version/go-linkchecker_${Version}_windows_amd64.zip"
Expand-Archive "go-linkchecker_${Version}_windows_amd64.zip" -DestinationPath .
.\go-linkchecker.exe --version

Install With Go

go install github.com/srmdn/go-linkchecker@latest

Build From Source

git clone https://github.com/srmdn/go-linkchecker.git
cd go-linkchecker
go build -o go-linkchecker .

Usage

go-linkchecker [flags] <directory>

Scan current directory:

go-linkchecker .

Scan a specific blog content directory:

go-linkchecker ./content/blog

Only show broken links:

go-linkchecker --only-broken ./content/blog

Save report to file:

go-linkchecker --only-broken --output report.txt ./content/blog

Show the installed version:

go-linkchecker --version

Skipping URLs

Use --skip-pattern to skip URLs you don't want checked, and --ignore-status to treat specific HTTP status codes as OK. Skipped URLs still appear in the report under a SKIPPED section so you always have full visibility; they are not silently hidden.

Common reasons to skip a URL:

  • Bot-hostile sites - some sites return HTTP 403 to all automated requests even though the page is live. They aren't broken, just blocking crawlers. Common examples: Wikipedia, OpenAI, Cloudflare community forum (community.cloudflare.com).
  • Affiliate or redirect links - short links that redirect to third-party destinations you don't control. See also --no-follow-redirects.
  • Local/dev URLs - localhost, 127.0.0.1, staging domains.
# Skip local URLs
go-linkchecker --skip-pattern "localhost|127\.0\.0\.1" ./content/blog

# Skip known bot-hostile domains
go-linkchecker --skip-pattern "wikipedia\.org|openai\.com|community\.cloudflare\.com" ./content/blog

# Combine multiple patterns
go-linkchecker --skip-pattern "localhost|wikipedia\.org|openai\.com|yourshortlinks\.com" ./content/blog

# Ignore common bot-blocking statuses
go-linkchecker --ignore-status 401,403 ./content/blog

# Combine skip pattern and status ignores
go-linkchecker --skip-pattern "localhost|wikipedia\.org|openai\.com" --ignore-status 401,403 ./content/blog

If you use a URL shortener or affiliate links that redirect to bot-hostile destinations, use --no-follow-redirects instead. This treats any HTTP 3xx response as OK without following the chain:

go-linkchecker --no-follow-redirects ./content/blog

The pattern is a regular expression matched against the full URL. Dots in domain names should be escaped (\.).

Report Format

The report has three sections:

Checked: 24 | Broken: 1 | OK: 23 | Skipped: 3
------------------------------------------------------------

BROKEN LINKS (1)

  [HTTP 404]
  https://example.com/old-page
  File: ./content/blog/my-post/index.md

------------------------------------------------------------

OK LINKS (23)

  [200] https://github.com/...
      File: ./content/blog/my-post/index.md
  ...

------------------------------------------------------------

SKIPPED LINKS (3)
(matched --skip-pattern or --ignore-status, not checked)

  https://wikipedia.org/...
      File: ./content/blog/my-post/index.md
  ...
  • Broken - checked and returned 4xx/5xx or a network error
  • OK - checked and returned 2xx/3xx
  • Skipped - matched --skip-pattern or --ignore-status, not checked

Use --only-broken to hide the OK and Skipped sections (useful for email reports or CI).

Example terminal report output:

go-linkchecker terminal report example

Email Reports

Pass SMTP credentials via flags or environment variables:

export LINKCHECKER_SMTP_HOST=smtp.example.com
export LINKCHECKER_SMTP_PORT=465
export LINKCHECKER_SMTP_USER=user@example.com
export LINKCHECKER_SMTP_PASS=yourpassword
export LINKCHECKER_SMTP_FROM="Link Checker <user@example.com>"
export LINKCHECKER_SMTP_TO=you@example.com

go-linkchecker --only-broken ./content/blog

By default, email is only sent if broken links are found (--email-only-broken=true). Set --email-only-broken=false to always send.

Email delivery includes:

  • Plain-text fallback for simple clients
  • Styled HTML layout for inbox readability and screenshots
  • Clear summary counts for checked, broken, healthy, and skipped links
  • Action-focused broken-link section
  • Relative file paths in the email body when files are inside the scanned directory

All Flags

Flag Default Description
--timeout 10s HTTP request timeout per link
--concurrency 5 Concurrent link checks
--only-broken false Only show broken links in report
--skip-pattern `` Regex - skip matching URLs (shown as Skipped in report)
--ignore-status `` Comma-separated HTTP status codes to treat as OK
--no-follow-redirects false Treat HTTP 3xx as OK - do not follow redirects
--output `` Write report to file
--smtp-host `` SMTP host
--smtp-port 465 SMTP port (TLS)
--smtp-user `` SMTP username
--smtp-pass `` SMTP password
--smtp-from `` From address
--smtp-to `` Recipient address
--email-only-broken true Only email if broken links exist
--version false Print version and exit

Release Binaries

Tagged releases build and publish binaries for:

  • linux/amd64
  • linux/arm64
  • darwin/amd64
  • darwin/arm64
  • windows/amd64

Each release includes archives and a checksums.txt file for verification.

Automating with systemd

Example weekly timer on a Linux server:

/etc/systemd/system/linkchecker.service

[Unit]
Description=Weekly link checker

[Service]
Type=oneshot
User=youruser
WorkingDirectory=/your/site/dir
EnvironmentFile=/etc/linkchecker.env
ExecStart=/usr/local/bin/go-linkchecker --only-broken --skip-pattern "localhost|wikipedia\.org" ./content/blog
StandardOutput=journal
StandardError=journal

/etc/systemd/system/linkchecker.timer

[Unit]
Description=Weekly link checker timer
Requires=linkchecker.service

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target
systemctl enable --now linkchecker.timer

Exit Codes

Code Meaning
0 All checked links healthy (skipped links do not affect exit code)
1 One or more broken links found

License

MIT

About

Fast, zero-dependency Markdown link checker in Go with concurrent checks and CI-friendly output.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors