Lychee: a link checker

Detects broken links…and broken anchor fragments, a boon for doc sites.

Lychee is a link checker that has a hidden superpower for doc sites. It can verify anchor fragments; that is, it can detect broken links to specific sections in a webpage.

This article provides a high-level introduction to lychee, one designed to help you get started quickly. Once you have the basics, use the lychee docs for more details and options.

Quick setup

Use your system’s package manager to install lychee. Mac users can use Homebrew:

$ brew install lychee

The lychee docs provide examples for many common environments, including Docker. The project’s README file [Github] describes additional options.

Check a single file

To check links in a single webpage, specify the URL:

% lychee https:\\example.com
  98/98 ━━━━━━━━━━━━━━━━━━━━ Finished extracting links
   🔍 98 Total (in 1s) ✅ 98 OK 🚫 0 Errors

Here, lychee checked 98 links and found no errors on the webpage.

Check multiple files

While lychee checks individual URLs, it can recursively check local files.

To do this, specify the folder where lychee should begin.

% lychee public/
// details clipped
✖ 86 errors, 32 warnings and 52 suggestions in 85 files.

When you run this command, it’s possible you’ll receive a series of errors:

   [WARN] Error creating request: InvalidPathToUri("/")
   [WARN] Error creating request: InvalidPathToUri("/about/")
   [WARN] Error creating request: InvalidPathToUri("/tips/")
   [WARN] Error creating request: InvalidPathToUri("/scribbles/")

Lychee needs a bit of help to resolve relative file references.

Use the --root-dir option to specify a folder to use as a base for relative paths:

lychee public/ --root-dir "$(pwd)/public/"

In this example, lychee runs in the parent directory of the public/ folder. Adjust the value of root-dir to fit your specific context.

Note that --root-dir requires a fully qualified path. Relative paths, such as ~/myproject/public, are not supported.

If you’re verifying links in a local preview, you might need to set a base URL instead:

lychee --base-url "https://localhost:1234/" "$(pwd)/public/"  

The port number varies from tool to tool; use the port appropriate for your environment.

A number of factors influence lychee’s results in such cases. Experiment to find the right settings for your needs.

Check local files only

Sometimes, you want to verify your local site links; that is, you want to verify your internal references without checking external links.

Use the --offline option to do this:

lychee public/ --root-dir "$(pwd)/public/" --offline

Lychee skips (excludes) external links while processing local ones.

Check anchor fragments

Use the --include-fragments option to verify anchor fragment links.

lychee public/ --root-dir "$(pwd)/public/" --include-fragments

When this option is active, lychee displays errors for broken anchor fragments:

[ERROR] file://.../public/about#attributions | Cannot find fragment

Why this helps

Anchor fragments (also called anchor links, fragment IDs, or header IDs) refer to specific sections in a webpage. In a traditional URL, they appear after a number sign.

To illustrate, consider the following URL:

https://localhost:1234/about#attributions

Here, attributes is an anchor fragment. When clicked, your browser opens the about page and moves the Attributions section into view.

Doc websites frequently use anchor fragments to direct readers to specific sections in other articles.

Many modern tools automatically define anchors for header content. The names of these anchors are based on the text of the header.

If you change the text of the header, it changes the underlying anchor, which in turn breaks existing links to the previous header.

Unlike other broken links, broken anchor links do not generate HTTP errors, which means they’re harder to detect.

Without a link checker like lychee, you have to verify anchor links by hand, which is error-prone, tedious, and time-consuming.

Lychee helps simplify the process by displaying broken anchor links as errors.

Lychee can also be automated so that link issues are more visible. To learn more, see Anchor links [lychee.cli.rs].

By default, lychee only reports problems. It doesn’t tell you about working links.

You can use the --verbose to list all links checked.

lychee --verbose http://localhost:1234/index.html
     [200] http://localhost:1234/scribbles/thoughts/bingo-card-redux/
     [200] http://localhost:1234/tips/web/css/newspapers-columns/
     [200] http://localhost:1234/tips/doc/hugo/
     // *results trimmed*
🔍 99 Total (in 0s) ✅ 99 OK 🚫 0 Errors

Here, lychee checks a locally built-version of the webpage and reports the status of each one.

Show debug messages

Lychee supports an extended verbose mode (-vv) that adds debugging messages to the results:

% lychee -vv http://localhost:1234
   // *results trimmed*
   [DEBUG] Redirecting to http://localhost:1234/tips/doc/tools/hugo/
   [DEBUG] Redirecting to http://localhost:1234/tips/javascript/nodejs/
   [DEBUG] Redirecting to http://localhost:1234/tips/doc/tools/mkdocs/
   [DEBUG] Redirecting to http://localhost:1234/about/privacy-policy/

In this example, the debug messages suggest links that redirected to a different location.

Strictly speaking, the links work (because they goes to valid webpages), however, redirect messages should be investigated as warnings, just in case there are other problems that need to be handled.

Lychee lets you extract links (and inputs) without checking them, which in turn can help a variety of maintenance tasks.

Use:

  • --dump to extract links from the input files

  • --dump-inputs to list the files containing links

You can combine these options in a number of interesting ways. The following example shows one way to build a sorted list of unique links used in your project:

% lychee public/ --root-dir "$(pwd)/public" \
      --offline --include-fragments --dump | sort | uniq

Lychee results

In general, Lychee reports status as either an HTTP error code or an environmental condition.

The following list shows a sample of unique messages seen over a period of days:

[200] file://.../gitrepos/example/public
[403] https://example.com/ | Rejected status code 
   (this depends on your "accept" configuration): Forbidden
[404] http://example.org/file.php | Rejected status code 
   (this depends on your "accept" configuration): Not Found
[410] http://example.com/articles/quick-setup | Rejected status code 
   (this depends on your "accept" configuration): Gone
[503] https://example.com/resource/ | Rejected status code 
   (this depends on your "accept" configuration): Service Unavailable

[DEBUG] Redirecting to http://localhost:1234/redirect/example/
[ERROR] file://.../content/example/badpath | Cannot find file
[ERROR] https://example.com/filefound#brokenfragment | Cannot find fragment
[ERROR] https://localhost:1234/ | Error (cached)
[ERROR] https://localhost:1234/ | Network error: error sending request for url 
   (https://localhost:1234/) Maybe a certificate error?
[WARN] Error creating request: InvalidPathToUri("/about")
[WARN] There were issues with GitHub URLs. You could try 
   setting a GitHub token and running lychee again.
[EXCLUDED] http://localhost:1234/
[TIMEOUT] https://example.com/?id=xyzzy | Timeout

Each line reports a status and provides appropriate context. The link target is generally presented as lychee encountered it.

The numeric codes represent HTTP status codes:

  • [200] indicates a working link, generally displayed in verbose mode.

  • [403] codes are “access forbidden” messages and have multiple causes.

    The site might require authentication or it might choose to deny access to automated tools like Lychee.

  • [404] errors are broken links that need to be investigated and resolved.

  • [410] errors indicate broken links, but are a special case.

    This error indicates the resource once existed, but it has been permanently removed.

  • [503] errors indicate a configuration issue with the host of a target URL.

Lychee results also include:

  • [DEBUG] messages when using debug mode (-vv)

  • [ERROR] messages caused by configuration or environmental issues. Use the details to troubleshoot.

  • [WARN] messages generally indicate configuration glitches.

    If the details include InvalidPathToUri, try specifying a base path.

  • [EXCLUDED] generally occur as a result of specific configuration settings.

  • [TIMEOUT] errors occur when the target host fails to respond in a reasonable time.

If you do not have access to a link target host, there’s not a lot you can do to resolve errors. In such cases, consider removing or replacing the URL with another resource.

In recent months, some sites have begun denying access to command-line tools such as lychee.

Consider testing 403 link errors by hand or adjusting lychee’s user agent string.

To learn more about lychee results and how to manage them, see the Lychee docs

Vital statistics

  • 6 September 2025: First post to this site.