CLI tool
gjtorikian/html-proofer avatar
gjtorikian/html-proofer

HTMLProofer checks what a static site links to, and swaps URLs when the file disagrees with the server

Test your rendered HTML files to make sure they're accurate.

1,642 stars216 forksRubyMIT

At a glance

What is it?
HTMLProofer is a Ruby test suite for rendered HTML: images, links, scripts, favicons and OpenGraph metadata. Its real configuration work is swap_urls and swap_attributes, because what a build emits and what a server serves are rarely the same string.
Who is it for?
Adopt HTMLProofer in continuous integration for a static site or any build step that emits HTML, since it targets documented standards rather than style opinions and its defaults are reliable enough for a build gate. Do not reach for it to check spelling or indentation, which the project explicitly excludes as subjective and prone to false positives.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Scope: documented standards, deliberately excluding opinionated checks

The project draws its own boundary early. In scope is any well-known, widely-used test for HTML document quality, and the stated rationale for that constraint is continuous integration: results have to be reliable, so the project balances correctness over performance and tries to trace every detection back to documented best practices or a specification such as W3.

What is deliberately out of scope is just as informative. Spell checkers and indentation checkers are named as subjective tests prone to false positives, and rather than dropping the idea the README points contributors at writing a third-party module instead.

That is a coherent position for a build gate. A checker that fails a deploy over a hyphenation preference gets disabled, and once disabled it checks nothing. HTMLProofer would rather check fewer things reliably.

The other scope note is about who can use it. The command line program is aimed at front-end developers, while advanced configuration requires Ruby, which is an honest description of a gem whose real surface is Ruby objects.

What the checks cover, element by element

The checks are grouped by HTML element, which makes it easy to see what a failure means.

For img elements: whether every image has an alt tag, whether internal image references are broken, whether external images are showing, and whether the images are HTTP.

For a and link elements: whether internal links work, whether internal hash references such as #linkToMe resolve, whether external links work, whether links are HTTPS, and whether CORS with SRI is enabled.

For script elements: internal script references resolve, external scripts load, and CORS with SRI is enabled.

Then two metadata checks that are easy to forget exist: favicons must be valid, and the images and URLs in OpenGraph metadata must be valid.

The HTTPS checks are worth noting as a category. A link checker that only validates reachability will happily pass an http:// URL, and HTMLProofer treats scheme as part of correctness.

Four entry points: file, one directory, many directories, bare URLs

The Ruby API offers four ways to say what to check, which is more flexibility than most people need and more than they discover.

A single file uses check_file:

ruby
HTMLProofer.check_file("/path/to/a/file.html").run

A directory uses check_directory, and several directories take an array:

ruby
HTMLProofer.check_directory("./out").run
ruby
HTMLProofer.check_directories(["./one", "./two"]).run

And check_links runs the same checks against URLs with no local files involved, which is how you would audit a page you do not build:

ruby
HTMLProofer.check_links(["https://github.com", "https://jekyllrb.com"]).run

Each returns an object you call run on. That split between constructing the runner and executing it is what lets a Rake task or a test framework drive it.

The gem also installs an htmlproofer executable, so the same checks are reachable without writing Ruby.

swap_urls and swap_attributes: reconciling the file with the server

The most useful configuration in this tool exists because what your HTML says and what your server serves are frequently different strings.

swap_urls maps a URL found in a file to the URL you want checked. The README's example uses a regular expression as the key:

ruby
run_proofer(file, :file, swap_urls: { %r{^https//placeholder.com} => "https://website.com" })

Any link matching that pattern is rewritten before being fetched.

swap_attributes does the same job for attributes rather than URLs, which is what you need for lazy-loading images where the real source is not in src:

ruby
run_proofer(file, :file, swap_attributes: { "img"  => [["data-src", "src"]] })

The value is an array of arrays, so several swaps per element can be given at once.

Together these two options are the difference between a checker that runs cleanly on day one and one you disable after a week of false failures. Note the asymmetry in the first example, where the pattern reads ^https//placeholder.com with a single slash. That is exactly what the README shows, and it is worth checking against your own URL when you copy it.

The command line's array syntax has its own rules

The executable takes flags, and the flag values are not uniform, which is where most friction comes from.

Options taking an array of values need quoting with no spaces. Status codes to ignore look like this:

bash
htmlproofer --ignore-status-codes "999,401,404" ./out

Options taking an array of regular expressions use a slash-separated syntax:

bash
htmlproofer --ignore-urls "/www.github.com/,/foo.com/" ./out

swap-urls is a pair of RegEx:String values and needs escaped colons so the separator is unambiguous:

bash
htmlproofer --swap-urls "wow:cow,mow:doh" --extensions .html.erb --ignore-urls www.github.com ./out

A third category needs well-formed JSON: --typheous, --cache and --swap-attributes. Remember that one before you file a bug about a rejected value.

The --extensions flag is how you point it at non-.html output, which matters for template files such as .html.erb.

Jekyll baseurl and the ignoring escape hatches

A Jekyll site with a baseurl produces links that look broken locally and work in production. The documented fix is swap_urls again, stripping the baseurl prefix so validation matches the generated path:

bash
htmlproofer --assume-extension ./_site --swap-urls '^/BASEURL/:/'

The same thing from a Rakefile:

ruby
require "html-proofer"

task :test do
  sh "bundle exec jekyll build"
  options = { swap_urls: "^/BASEURL/:/" }
  HTMLProofer.check_directory("./_site", options).run
end

For content that should never be checked, data-proofer-ignore on a tag excludes it from every check:

html
<a href="https://notareallink" data-proofer-ignore>Not checked.</a>

It inherits, so wrapping a whole subtree works and reaches all the way up to the html tag.

There is a third case, for a pull request that links to files not published yet. The README suggests diffing against the base branch with git merge-base and git diff to collect the new files and exclude them, which keeps a branch green until its content is actually deployed.

Installation, Nokogiri, and where the cost lands

It is an ordinary gem. Add it to the Gemfile and run bundle install:

bash
gem install html-proofer

The performance note in the README matters for continuous integration: set NOKOGIRI_USE_SYSTEM_LIBRARIES to true in the environment to speed up installation, using the system Nokogiri libraries instead of building them. On a CI runner that recompiles native extensions on every job, this is the difference between a fast build and a slow one.

If installing Ruby and Nokogiri is itself the problem, the command line tool can be run through Docker, using the klakegg/html-proofer image referenced in the README.

The repository is a standard gem layout with a gemspec, a Rakefile, a.rubocop.yml, a pinned.ruby-version, and separate UPGRADING.md and CHANGELOG.md files. The presence of a dedicated upgrading document is a good sign for a tool people wire into builds.

Releases v5.2.0 on 2026-01-07, v5.2.1 on 2026-03-29 and v5.2.2 on 2026-07-28 show a project on a 5.x line with steady patch work. The last push was on 2026-10-16, the repository is not archived, and the licence is MIT.

Editorial conclusion

Adopt HTMLProofer in continuous integration for a static site or any build step that emits HTML, since it targets documented standards rather than style opinions and its defaults are reliable enough for a build gate. Do not reach for it to check spelling or indentation, which the project explicitly excludes as subjective and prone to false positives. Before wiring it in, set swap_urls for your baseurl and data-proofer-ignore on any URL that only resolves in production, because the checker sees the generated file rather than the deployed site.

Frequently asked questions

How do I install HTMLProofer?

Add gem 'html-proofer' to the Gemfile and run bundle install, or install it directly with gem install html-proofer. For faster installs, especially in CI, set NOKOGIRI_USE_SYSTEM_LIBRARIES to true in the environment. A Docker image is also available if you cannot install Ruby and Nokogiri.

What does the swap_urls option do in HTMLProofer?

It maps a URL found in the file to the URL that should actually be checked, using a regular expression or string as the key. It is the documented way to handle a Jekyll site baseurl and placeholder URLs.

How do I stop HTMLProofer checking a link?

Add the data-proofer-ignore attribute to the tag, or to any parent element up to the html tag, and it is excluded from every check. New files in a pull request can instead be excluded by diffing against the base branch.

Does HTMLProofer check spelling or indentation?

No. The project excludes subjective checks such as spell checkers and indentation checkers because they are prone to false positives in continuous integration, and points contributors at third-party modules instead.

What file types does HTMLProofer check?

The checks cover img elements, a and link elements, script elements, favicons and OpenGraph metadata, covering alt tags, internal and external references, hash references, HTTPS usage and CORS with SRI. Use the --extensions flag to point it at output such as .html.erb.

Official sources

  1. gjtorikian/html-proofer on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gjtorikian-html-proofer.svg)](https://hysenlabs.com/projects/gjtorikian-html-proofer)

Was this useful?