htmlproofer-action¶
A GitHub Action that runs HTMLProofer against a built static site. Its defaults are tailored to Jekyll sites hosted on GitHub Pages.
Usage¶
Add the action after the step that builds your site:
HTMLProofer can cache link-check results. Persisting that cache reduces repeated external requests:
- name: Restore HTMLProofer cache
uses: actions/cache/restore@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ runner.os }}-htmlproofer-
- name: Check site
uses: athackst/htmlproofer-action@main
- name: Save HTMLProofer cache
if: always()
uses: actions/cache/save@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
Inputs¶
The defaults below are the effective defaults when the project is run as a
GitHub Action. Boolean inputs accept true or false. List inputs accept
comma-separated or newline-separated values.
Site and checks¶
| Input | Description | Default |
|---|---|---|
directory |
Directory containing the built site | ./_site |
allow_hash_href |
Treat href="#" as valid |
true |
allow_missing_href |
Allow a elements without an href |
false |
assume_extension |
Add this extension when resolving extensionless internal URLs | .html |
check_favicon |
Check whether favicons are valid | false |
check_links |
Check a and link elements |
true |
check_images |
Check img elements |
true |
check_scripts |
Check script elements |
true |
check_opengraph |
Check images and URLs in Open Graph metadata | false |
check_external_hash |
Check fragments on external URLs | true |
check_internal_hash |
Check fragments on internal URLs | true |
check_sri |
Require SRI on external link and script resources |
false |
directory_index_file |
File used when a URL refers to a directory | index.html |
disable_external |
Disable external URL checks | false |
enforce_https |
Fail HTTP links | true |
extensions |
File extensions to check, including the leading dot | .html |
ignore_empty_alt |
Allow images whose alt attribute is empty |
true |
ignore_missing_alt |
Allow images whose alt attribute is missing |
false |
ignore_empty_mailto |
Allow mailto: links without an email address |
false |
ignore_files |
File paths or /regular expressions/ to skip |
none |
ignore_status_codes |
HTTP status codes to ignore | none |
ignore_urls |
Additional URLs or /regular expressions/ to skip |
none |
ignore_common |
Ignore URLs that commonly reject automated link checks | true |
ignore_new_files |
On pull requests, ignore new or renamed files detected by Git | false |
HTMLProofer treats empty and missing alt attributes independently.
ignore_empty_alt controls empty attributes, while ignore_missing_alt
controls missing attributes.
When ignore_common is enabled, the action entrypoint adds
https://fonts.gstatic.com to ignore_urls. Set ignore_common: false to
check that URL normally. User-provided ignore_urls are preserved in either
mode.
URL mapping¶
| Input | Description | Default |
|---|---|---|
host |
Public host used to resolve absolute site URLs as local | ${{ github.repository_owner }}.github.io |
base_path |
Path below the host where the site is published | /${{ github.event.repository.name }} |
site_url_swap |
Generate URL substitutions from host and base_path |
true |
swap_urls |
Additional URL substitutions in regular-expression:replacement form |
none |
When site_url_swap is enabled, the action entrypoint generates substitutions
that remove the configured host and base path before HTMLProofer resolves local
URLs. User-provided swap_urls entries are added to those substitutions.
Separate multiple entries with commas or newlines. Escape a literal colon as
\:.
HTTP and action behavior¶
| Input | Description | Default |
|---|---|---|
max_concurrency |
Maximum number of concurrent HTTP requests | 2 |
connect_timeout |
Connection timeout in seconds | 10 |
followlocation |
Follow HTTP redirects | true |
ssl_verifypeer |
Verify the remote TLS certificate | false |
ssl_verifyhost |
curl host-verification setting | 0 |
timeout |
HTTP request timeout in seconds | 30 |
retries |
Total attempts before the action fails | 6 |
retry_wait |
Seconds to wait between failed attempts | 60 |
cache |
JSON object configuring HTMLProofer's cache; use an empty input to disable it | { "timeframe": { "external": "2w", "internal": "1w" } } |
gh_token |
Token used to inspect pull-request changes for ignore_new_files |
${{ github.token }} |
Caution
TLS peer and host verification are disabled by default. Set
ssl_verifypeer: true and ssl_verifyhost: 2 when the sites you check use
publicly trusted certificates.
Deprecated inputs¶
These aliases remain available for compatibility but should not be used in new workflows:
| Deprecated input | Replacement |
|---|---|
check_html |
check_links |
check_img_http |
check_images |
empty_alt_ignore |
ignore_empty_alt |
missing_alt_ignore |
ignore_missing_alt |
url_ignore, url_ignore_re |
ignore_urls |
url_swap |
swap_urls |
internal_domains and the misspelled max_paralell are accepted by the action
metadata but no longer affect HTMLProofer.
The HTMLProofer options log_level, only_4xx, and swap_attributes are not
currently exposed as action inputs.
Examples¶
MkDocs¶
name: Check MkDocs site
on:
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Build documentation
uses: athackst/mkdocs-simple-plugin@main
- name: Restore HTMLProofer cache
uses: actions/cache/restore@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ runner.os }}-htmlproofer-
- name: Check site
uses: athackst/htmlproofer-action@main
with:
directory: site
- name: Save HTMLProofer cache
if: always()
uses: actions/cache/save@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
Jekyll and GitHub Pages¶
actions/configure-pages provides the correct host and base path for user,
organization, and project sites:
name: Build and check Jekyll site
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Configure Pages
id: pages
uses: actions/configure-pages@v5
- name: Build
uses: actions/jekyll-build-pages@v1
- name: Restore HTMLProofer cache
uses: actions/cache/restore@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ runner.os }}-htmlproofer-
- name: Check site
uses: athackst/htmlproofer-action@main
with:
host: ${{ steps.pages.outputs.host }}
base_path: ${{ steps.pages.outputs.base_path }}
- name: Save HTMLProofer cache
if: always()
uses: actions/cache/save@v6
with:
path: tmp/.htmlproofer
key: ${{ runner.os }}-htmlproofer-${{ github.run_id }}-${{ github.run_attempt }}
- uses: actions/upload-pages-artifact@v3
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
Ignore URLs¶
Plain strings are matched literally. Surround an entry with / to treat it as
a regular expression:
- uses: athackst/htmlproofer-action@main
with:
ignore_urls: |
/https:\/\/(www\.)?twitter\.com/
https://example.com/expected-404
Add URL substitutions¶
This removes version prefixes from root-relative URLs:
Configure or disable the HTMLProofer cache¶
- uses: athackst/htmlproofer-action@main
with:
cache: '{ "timeframe": { "external": "1w", "internal": "3d" } }'
To disable HTMLProofer caching, pass an empty value and omit the actions/cache
step:
When caching is enabled, the action prints cache statistics in its log and in the GitHub step summary.
The examples use separate restore and save actions so the cache is saved even
when HTMLProofer reports broken links. HTMLProofer writes cache.json before
reporting its failures, and if: always() lets the save step run after the
failed check.
The run ID and attempt number make each saved key unique, including workflow reruns. The restore prefix selects the newest cache visible to the current branch or pull-request scope. GitHub restricts cache access by Git ref, so a cache created for one pull request is not available to a different pull request.
Local Docker usage¶
The Docker image runs from /site, so mount the built site there. GitHub
expressions from action.yml are unavailable locally; pass any required inputs
as INPUT_* environment variables. The entrypoint enables site_url_swap by
default, but generates no substitutions when INPUT_HOST is empty.
docker run --rm \
--volume "$PWD/_site:/site" \
--env INPUT_HOST=example.github.io \
--env INPUT_BASE_PATH=/example \
althack/htmlproofer:latest
When used directly through Docker, directory defaults to ., retries default
to 1, caching is disabled, and host and base_path are empty. The
HTMLProofer option defaults implemented by the Ruby library are the same as
those listed above.
Where defaults live¶
There are two runtime layers:
- The Ruby library owns HTMLProofer defaults such as enabled checks, concurrency, and HTTP timeouts. These defaults also apply to direct Docker usage and unit tests.
action.ymlowns GitHub-specific and wrapper defaults such as repository expressions, retry count, token, cache policy, and the default build directory.
Keeping a Ruby-owned default out of action.yml avoids two sources of truth:
an omitted input reaches the library as unset, and the library applies its
default. If a library default is repeated in action.yml, GitHub always sends
that value and can hide later library changes. The tradeoff is that GitHub's
Marketplace UI does not display those implicit defaults, so this table is the
complete user-facing reference.
License¶
This software is licensed under the Apache License 2.0.