Automatic link checker

Broken links are a common problem, particularly for a website which is already old and of medium size, which is an annoyance for visitors, for search engine optimisation, and for webmasters. While there are good tools available to check this point, it would be better to have something entirely automatic, so a webmaster does not even have to think about this point.

To handle this issue, I configured a GitHub action which checks for broken links 1/ when pushing a new version of the website and 2/ from time to time to avoid link rot.

A first version of the link checker system used the lychee link checker because it was an easy pick with a ready-made GitHub action. However, it handled some edge cases poorly, like delayed responses, so I had to choose another tool.

After some benchmarks, I settled on LinkChecker, a mature tool written in Python.

Wrapper script

In addition to the mere LinkChecker program, I needed some convenience scripts to:

At the beginning, there were several bash scripts using grep/sed/awk. Then for CSV parsing the bash scripts used inline Python. Then I reworked these to use only a single Python script, available as linkchecker.py in the repository.

GitHub action file

The GitHub action file allows checking for broken links 1/ when changes are made and 2/ monthly, on the first day of each month, to avoid link rot. Additionally, GitHub sends me an email when this action fails so I’m warned that I have to update links.

According to the latest statistics, out of 655 links checked, 25 links are ignored, falling into the following cases:

Cache

During development, it is common to run the test script many times. Checking all URLs each time would be inconvenient and risk triggering various anti-bot protections. A cache mechanism is therefore implemented to check external links only when needed.

Post-checking

LinkChecker triggers some anti-bot protections on certain sites. A post-checking phase is performed using primp. A previous version used curl_cffi, but following some doubts about curl_cffi, I preferred to switch to primp.

Cloudflare detection

In previous versions, I had a manual list of Cloudflare URLs to ignore due to Cloudflare’s bot block. Now, URLs blocked by Cloudflare are automatically flagged as such and cached so they are not checked again for 1 month, to avoid tickling the dragon too much.

Summary page

The script also produces a very convenient summary page for display on GitHub actions pages, like the one shown below (slightly older, with 26 ignored links instead of the current 25):

Summary page generated by the link checker, showing statistics, redirects, ignored, and cached links.