Linkinator Action
August 29, 2026 · View on GitHub
Find broken links in Markdown, HTML, and websites—directly in GitHub Actions.
Linkinator Action is the GitHub Actions wrapper for Linkinator. It checks local documentation and remote URLs, annotates failures in the workflow log, and writes a readable job summary.

Quick start
Add .github/workflows/links.yml to your repository:
name: Check links
on:
pull_request:
push:
branches: [main]
jobs:
linkinator:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: JustinBeckwith/linkinator-action@v2
That is it. By default, the action checks Markdown files matching *.md in the repository root and fails the job when it finds a broken link.
To scan every Markdown file in the repository:
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: '**/*.md'
What you get
- Broken-link annotations in the workflow log
- A GitHub Actions job summary grouped by source file
- Support for local files, remote pages, sitemaps, redirects, fragments, and CSS URLs
- Configurable retries, timeouts, status-code policies, and skip patterns
- A machine-readable
resultsoutput for later workflow steps
Common recipes
Check anchors and fragments
Fragment checking is opt-in because it requires downloading and parsing page content:
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: '**/*.md'
checkFragments: true
Skip unreliable or private URLs
linksToSkip accepts comma- or whitespace-separated regular expressions. Skip rules are matched against the complete URL, including fragments.
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: '**/*.md'
linksToSkip: >-
^https://example\.com/private
^https://(?:www\.)?ghostbrowser\.com(?:/|$)
.*#L[0-9]+(?:-L[0-9]+)?$
skip is supported as an alias for linksToSkip.
Retry transient failures
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: '**/*.md'
timeout: 15000
retry: true
retryErrors: true
retryErrorsCount: 3
retryErrorsJitter: 2000
retry handles HTTP 429 responses with a retry-after header. retryErrors handles network errors and 5xx responses.
Customize status-code handling
Map an exact status or a status family to ok, warn, skip, or error:
- uses: JustinBeckwith/linkinator-action@v2
with:
statusCodes: '{"403":"warn","429":"skip","5xx":"warn"}'
Enforce HTTPS and flag redirects
- uses: JustinBeckwith/linkinator-action@v2
with:
requireHttps: error
redirects: warn
requireHttps accepts off, warn, or error. The boolean values true and false are aliases for error and off. redirects accepts allow, warn, or error.
Check a website recursively
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: https://example.com
recurse: true
concurrency: 20
Recursive scans follow links on the same root domain. Use a reasonable concurrency value when scanning a site you do not control.
Crawl pages from a sitemap
Set sitemap to load /sitemap.xml from each URL in paths and check every page it lists:
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: https://example.com
sitemap: true
Sitemap indexes are followed recursively. To use one or more custom sitemap locations, pass comma- or whitespace-separated URLs with sitemapUrl instead:
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: https://example.com
sitemapUrl: >-
https://example.com/docs-sitemap.xml
https://example.com/blog-sitemap.xml
sitemap and sitemapUrl cannot be combined. Sitemap mode checks links on the listed pages without recursively discovering unlisted pages; add recurse: true to crawl same-domain links from those sitemap seeds too.
Check CSS URLs
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: public
checkCss: true
This extracts URLs from CSS files, <style> blocks, and inline styles.
Support clean URLs and directory links
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: docs
cleanUrls: true
directoryListing: true
cleanUrls lets a link such as /about resolve to about.html. directoryListing lets local links to directories resolve through an automatically generated directory index and defaults to true.
Rewrite URLs in pull requests
The action automatically rewrites matching GitHub blob and tree links from the base branch to the pull request branch. You can also define one custom rewrite:
- uses: JustinBeckwith/linkinator-action@v2
with:
urlRewriteSearch: '^https://docs\.example\.com/'
urlRewriteReplace: 'https://preview.example.com/'
Both rewrite inputs must be provided together.
Configuration file
For a larger configuration, add linkinator.config.json to the repository:
{
"recurse": false,
"concurrency": 20,
"skip": [
"^https://example\\.com/private",
"^mailto:"
],
"statusCodes": {
"403": "warn",
"429": "skip",
"5xx": "warn"
}
}
Then reference it from the workflow:
- uses: JustinBeckwith/linkinator-action@v2
with:
paths: '**/*.md'
config: linkinator.config.json
Values supplied directly to the action take precedence over the configuration file. See the Linkinator API options for the underlying configuration format.
Inputs
| Input | Default | Description |
|---|---|---|
paths | *.md | Comma- or whitespace-separated paths, globs, directories, or URLs to scan. |
config | linkinator.config.json when present | Path to a Linkinator configuration file. |
concurrency | 100 | Maximum number of concurrent requests. |
recurse | false | Follow same-domain links recursively. |
sitemap | false | Load /sitemap.xml from each HTTP path and use its pages as crawl seeds. |
sitemapUrl | — | Comma- or whitespace-separated explicit sitemap URLs; cannot be combined with sitemap. |
linksToSkip | — | Comma- or whitespace-separated URL regular expressions to skip. |
skip | — | Alias for linksToSkip. |
timeout | 0 | Request timeout in milliseconds; 0 disables the action-level timeout. |
markdown | true | Parse Markdown when scanning local files. |
serverRoot | repository root | Root from which local files are served. |
directoryListing | true | Generate directory listings for local directory links. |
retry | false | Retry 429 responses that include retry-after. |
retryErrors | false | Retry network errors and 5xx responses. |
retryErrorsCount | 3 | Maximum retries for network errors and 5xx responses. |
retryErrorsJitter | 2000 | Maximum retry-delay jitter in milliseconds. |
userAgent | Linkinator default | Custom HTTP User-Agent header. |
verbosity | WARNING | One of DEBUG, INFO, WARNING, ERROR, or NONE. |
urlRewriteSearch | — | Regular expression used for a custom URL rewrite. |
urlRewriteReplace | — | Replacement used with urlRewriteSearch. |
allowInsecureCerts | false | Allow invalid or self-signed TLS certificates. |
requireHttps | false (off) | HTTPS policy: off, warn, or error. |
cleanUrls | false | Resolve extensionless local links to .html files. |
checkCss | false | Extract and check URLs found in CSS. |
checkFragments | false | Validate fragment identifiers and anchors. |
statusCodes | Linkinator defaults | JSON object mapping statuses or families to ok, warn, skip, or error. |
redirects | allow | Redirect policy: allow, warn, or error. |
Boolean inputs should be written as true or false. See action.yml for the canonical action metadata.
Outputs
The action exposes the full Linkinator result as results:
- id: links
uses: JustinBeckwith/linkinator-action@v2
- name: Inspect results
if: always()
env:
LINKINATOR_RESULTS: ${{ steps.links.outputs.results }}
run: echo "$LINKINATOR_RESULTS"
The result includes the overall pass/fail state and each checked link's URL, optional anchor displayText, status, state, parent, and failure details. Broken-link job summaries include the anchor text when Linkinator discovered the URL in an <a> element.
Troubleshooting
A valid URL reports a network failure
The action reports the underlying transport cause when available, such as ENOTFOUND, ECONNREFUSED, or a timeout. These failures can be specific to automated clients or GitHub-hosted runners.
- Add a reasonable
timeoutand enableretryErrorsfor transient services. - Set
verbosity: DEBUGto include complete error and cause details. - Add persistently bot-protected or unreliable domains to
linksToSkip.
Relative directory links return 404
Keep directoryListing: true when local documentation links to directories without an index file. If links are resolved from the wrong location, set serverRoot to the directory that represents the site's root.
A skip pattern does not match
Skip values are regular expressions, not shell globs. Quote patterns in YAML, escape literal dots (example\.com), and remember that inputs are split on commas and whitespace. Use a configuration file when a pattern itself must contain either delimiter.
I need more logging
Set verbosity: DEBUG. To include GitHub runner diagnostics as well, enable debug logging in GitHub Actions.
Versioning and security
Use the moving major tag for automatic compatible updates:
- uses: JustinBeckwith/linkinator-action@v2
For a fully immutable workflow, pin the action to a commit SHA and let a dependency updater manage it. The action runs on the Node.js runtime bundled by GitHub Actions; consumers do not need to install Node.js themselves.
Contributing
Issues and pull requests are welcome. For changes to the underlying checker, visit the Linkinator repository.