Linkguard: catch dead links and swap in archived copies

· 3 min read

Every blog rots a little. The post you wrote two years ago still reads fine, but a third of its outbound links now land on a 404, a parked domain, or a redesign that ate the page. Linkguard is a free GitHub Action that finds those links in a Jekyll, Hugo, or folderblog site and points readers to an archived copy instead.

linkguard.persistwise.com · v0.1.0 on GitHub

What it does

Drop it into a weekly workflow and Linkguard walks your content, pulls every outbound link out of the markdown and HTML (skipping code blocks, comments, relative links, and private addresses), and checks each one. Every link comes back as one of four results:

  • ok works, or only moved from http to https
  • moved still works but redirects somewhere else, so you may want to update it
  • dead is a 404 or 410, or an outage that survived retries
  • blocked is bot protection (401, 403, 999), and Linkguard never calls those dead

For each link it also asks the Internet Archive’s Wayback Machine for a snapshot. When a dead link has a good one, the report hands you the archived URL. Results land in the run’s job summary as a table, as warning annotations on the exact source lines, and as JSON and markdown report files.

It checks politely: one request per host at a time, at most four in flight, retries with backoff, and Retry-After honoured.

Two modes, no account required

Lookup-only is the default. No secrets, nothing captured, just checks and existing snapshots.

Snapshot mode turns on when you add your own free Internet Archive keys as repo secrets. Linkguard then asks Save Page Now to capture working links that have no recent snapshot, so a copy exists before the page dies. Captures count against your own Internet Archive account and are paced to six a minute, so a first run over 100 links can take a while.

Fix it with a pull request

Set open-pr: true and Linkguard opens a pull request that replaces each dead link with its web.archive.org copy, exactly where it appears and nowhere else. The PR lists every swap and says plainly that each one is an archived copy, not the original. It never pushes to your default branch, so you review before anything changes.

Get started

# .github/workflows/linkguard.yml
name: Linkguard
on:
  schedule:
    - cron: '17 6 * * 1' # weekly
  workflow_dispatch:
permissions:
  contents: read
jobs:
  links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: ninepointlabs/linkguard@v0.1.0

It detects your site type from the repo layout, or you can pass site-type and your own paths. Ready-made workflows for Jekyll, Hugo, and folderblog are in the repo’s examples/ folder, and the README documents every input and output.

The free tier checks 100 unique links per run; anything past that is skipped, and the log says how many.

Honest limits and privacy

This is a first release. Snapshot capture and open-pr are covered by mocked tests so far, the Internet Archive doesn’t publish its rate limits, and its availability API sometimes misses a snapshot that exists. A link that’s down for a GitHub runner can still be up for people, so read the report before you merge.

There’s no telemetry and no Linkguard server. Only link URLs go to archive.org; your file contents never leave the runner. MIT licensed. Problems go in the issue tracker.