Show HN: Bernard – a link checker for your website (bernard.app)
GOAL: Bernard is an automated links checker that crawls your website daily, identifying broken links (internal and external), and missing redirects, such as after a redesign or URL change.
TARGET AUDIENCE: Anyone with a website—be it is a personal blog or company portal—looking to prevent link rot, to keep old URLs reachable and to avoid the dreaded 404 Page Not Found error.
STACK: Elixir, with Phoenix Live View for the interactive dashboard. It runs on PostgreSQL, hosted as Podman containers on a single dedicated Hetzner server.
PLAN: I am releasing this as a free, open beta to address the last few issues, and hope to introduce paid plans in February 2024. While there is a backlog of improvements and bug fixes, my primary focus now is making sure the product is aligned with what users need.
The pricing model is yet to be defined, but I'm considering a free tier for small website owners, and a usage-based plan starting from $10/mo for X,000 links checked per month. This would pair well with the upcoming REST API to provide a links-checker-as-a-service product to embed in third-party systems. (Feel free to contact me if you might be interested in using this API)
I also wrote about my journey to get this off the ground, and the challenges I faced at https://combo.cc/posts/bernard-devlog-3-pre-launch-reflectio...
Looking forward to your criticism and suggestions.
77 comments
[ 3.5 ms ] story [ 41.3 ms ] threadNow, regarding the pricing, i really don't like subscriptions, we already deal with a lot of subscriptions every month, but it's up to you.
I would suggest also to give ability to pay once for checking the links. So, when i come back maybe in 6 months later, i will again pay one, and so on, etc...
I would also like to download the link in an HTML page and consult them or do whatever stuff i like, for example, scrapping my own HTML to find broken link and do some action in development server or robot testing, but downloading shouldn't be free to
But it's nice, keep going and find your perfect market
Exporting the list in CSV format is planned in the very near future, along with better filtering tools, search and ignore patterns.
Thanks for taking the time to trying it out :)
I plan on writing a long post on my experience writing a web crawler in Elixir, and I believe it is a harder problem than many—including me one year ago—think it is. Web servers out there are just broken, pages have invalid HTML that somehow browsers are able to interpret, etc.
Here's some things Bernard does that many other don't:
* I respect robots.txt for each link I visit, and keep a single connection per {host,port} not to hammer popular domains. Maybe not ideal speed-wise, but I try to be a good Internet citizen.
* Modern MITM-like services such as Cloudflare, Akamai, etc. need to be accounted for, as they might return 403s even if the link is working perfectly. I am planning on registering as a "Cloudflare good bot" very soon, right now I simply ignore and log 403s for Cloudflare-server domains. The goal ultimately is to have as many false negatives as possible.
* In the future, Bernard will be able to parse CSS and JS files, to find and test links to @font-face URLs, imported JS scripts, etc.
* If there is enough demand, as it is quite involved infrastructure-wise, I might consider adding support for client-side only websites, through headless Chrome (on a separate, more expensive tier).
* Not shipped in this MVP, the original goal of Bernard is to remember all the links its seen, so it is able to notify you if one 404s because you changed its URL, moved it around, and forgot to set up a redirect. This happens far too often in my experience, breaking bookmarks and causing SEO issues.
And last, but not least, none of the alternative I have tried, freeware or FOSS, felt good, correct or well-designed enough to use, from my point of view. I do believe there is always a space to do better than the status quo.
I just did a new run of the test and the status information is now available.
I respect your experience on this project, but a suite (not just Chrome) of headless browser tests checking for broken links, amongst many other things, seems like not only the most sane way to approach this, but something that has been done to death on most serious web dev projects at $corporate_workplace. Obviously these are not open source projects so everyone else has to reinvent the wheel.
Parsing HTTP responses in your favorite language for tasks like this has never been reliable at any time in the entire history of web development. The myth that HTTP alone is all you need to understand the behavior of a website has always been wrong because it's far outside the scope of the spec. All of the link checkers in these comments seem like they'd be unreliable piles of edge case handling because the authors insist they should be able do it all from a Go or Python script or whatever.
I'm having a difficult time understanding why someone would pay for this or even want to run this anywhere but locally. Other than some individual's blog or affiliate marketing website I think most projects with a ton of links to check are already well supported by teams of devs who write tests like this all the time, and small time individuals with blogs wouldn't want to pay for yet another relatively expensive 3rd-party service.
Yes, in theory you would expect everyone to check their links. In practice even my non-tech mother knows what a 404 error is.
My initial target audience are web agencies, that either operate in a ship-ASAP-and-fix-problems-later way, or otherwise allow their non-technical people to create pages and upload images. Mistakes happen often, links break, and might stay broken for months before someone notices.
I haven't worked with Fortune 500 companies, so I don't know how they operate at that level, but pretty much no small/med company, including web agencies, has an automated stack of headless browsers checking for dead links. For most people, dead link testing is done once in a blue moon through the abysmal interface of Google Analytics.
EDIT: you added the paragraph about parsing HTTP responses after I have answered, so I wonder if we're talking past each other. This is not a simple bash script exactly BECAUSE the web is full of inconsistencies and broken servers, I am well aware of it. My bet is that someone wants to pay me to deal with all that nonsense, instead of using... I'm not even sure what is the state-of-the-art FOSS links checker. I wrote Bernard because I found none that was good enough.
That said, Cloudflare has a process to register as a "good bot" [1], which I believe we are (we respect robots.txt, keep a single connection alive per host, etc.), so it's possible that once I set that up, the gods that control the Internet might let us through.
In general though, Bernard follows the HTTP spec but cannot do miracles. If your webserver is trigger-happy or very strict, it is your responsibility to let us through if you want to use the service.
1: https://radar.cloudflare.com/traffic/verified-bots
Feel free to contact me if you might be interested in test driving it directly or through a REST API, which I'm not sure if there is any demand for.
I've been using Xenu's Link Sleuth (https://home.snafu.de/tilman/xenulink.html) forever, but I should probably try some others out and see if there's something better now. Xenu's is also a funny throwback to the old web, sort of like the Space Jam website.
[0] https://blogs.hyvor.com
The code in question, simplified:
One light bug: links to HN stories are marked as 403 forbidden (I guess the server blocks the crawler)
For the free tier: honestly I think you can charge even for small blog owners. I would pay a small yearly fee to make sure links are healthy
I've had a lot of URL encoding-related issues in the past 24 hours, due to Elixir's URI library being a little too strict, and a lot of websites having broken links that browsers are able to work around and interpret just fine. I reckon I will have to spend my weekend writing a bespoke URL encoder/decoder.
No doubt URL encoding is frustrating, good luck taking it on!
I'll add those in my test suite and fix them soon, thanks. Indeed Bernard should be able to replicate the behaviour of any browser, however broken the links are. If it works in your browser, it should work in Bernard.
BTW, even curl seems to have issues with those links:
Would love to know whether found a need to define any custom logic for 'international' websites?
For example a lot of global companies will have
www.theirdomain.com/example-page
But then also have
www.theirdomain.com/us/example-page www.theirdomain.com/au/example-page www.theirdomain.com/nz/example-page etc...
If you're crawling from one regional or the 'global' entry point you might not always find all of the regional variations of pages. Sometimes the site might redirect you to your previous region if you try to change.
A common tool SEO teams use is screaming frog which offers similar features, crazy that a web service isn't more commonly used for this kind of thing.
If the webserver redirects based on IP geolocation, it is planned way down the line to have a few Bernard workers spread around the globe, but I'll be honest, it's not my highest priority right now unless this is a common enough issue.
Big enterprises that might want this kind of thing are probably also the types of people that are least likely to use this kind of tool though.
> We have analysed 68 links on your website and haven't found a single issue.
I'd love to see what this actual list of links was, even if they weren't broken.
I'm partially curious because I personally use lychee [1] for this purpose, but when I run lychee (even while ignoring several classes of links), it is checking _significantly_ more links. E.g., the output looks like:
> 1401 Total (in 7s) 1361 OK 4 Errors 35 Excluded
Curious to understand what it is checking that Bernard is not.
[1]: https://github.com/lycheeverse/lychee
Seems like lychee also extracts links from text, Bernard does not. Only links inside HTML tags are considered, text nodes are ignored.
If I only run lychee on the same single page, the numbers are closer:
> 76 Total (in 1s) 73 OK 0 Errors 2 Excluded
So, if you add example.com, the crawl starts at / and all links found therein are added to the queue, and so on. For convenience www.example.com is also considered within crawl scope (and viceversa). Everything else is treated as an external link, so tested but not parsed.
Is it possible that your links are hosted on another subdomain than the one you have entered? I'll do a deeper dive into this issue tomorrow.
> I'll do a deeper dive into this issue tomorrow.
FWIW, I don't think I'd go as far as calling this an issue, I suspect both Bernard and Lychee are doing different, but reasonable things.
No need to spend much time on my account, and cheers on launching a useful project!
I really like your blog and find it encouraging with regard to other ideas I am thinking about.
I saw many broken links on users’ sites and wanted to do this myself, but didn’t have time to implement.
Unfortunately, many web servers will block bots, either immediately or after a few requests.
I'll have a look, thanks for the heads up.
edit: User-agent: bernard/1.0"
I bet thats going to cause issuses.
Id fake a browser user agent for off domain sites.
1: https://bernard.app/demo/8b13ab64-2440-4520-be05-905b5e2188f...
I like the product and I would use it.
Thanks for the bug report :) to be frank, there is actually a recursive link in your website (that "hooked on computer" link towards the top of https://keeb.dev/2022/05/28/The-beginning/)
The first is fine, the second needs special logic to tell that when we reach "/page/page/[...]/page" we should probably stop. I bet $5 that their web server log shows plenty of naive bots like mine getting lost in that recursion, burning unnecessary CPU time.
Thankfully, the solution is not that hard to implement.
It is on my todo list to fix. Thanks.
I've literally done this before.
This is an extremely simple product that could be written in a morning by someone competent.
Which is why there will always be a market for products like this. The point is that someone will pay a developer (someone competent) for software that just solves the problem for them.
Plenty of people run websites and have no idea how to even begin something like this.
The curse of knowledge we have in the bubble we're in makes these things appear simpler than they are to most people.
I am sorry, but you vastly underestimate the problem. Just read the other comments and see how many corner cases are there. It took me 3 days to implement a crawler than says OK on 200, and ERROR on 404, and 6 months to deal with exceptions to the rule, and apparently thanks to the feedback Ive received, there are many other exceptions to deal with.
Sorry, this is a bit like that old Dropbox comment. The thing is, if your website is easily covered by a 50-line Python script, you do not need Bernard.
maybe you're right though
I know Google webmaster (did they changed their name!) already has the info but it is not easy to use and is irritating to navigate around.
Anyways, signing up and best of luck to your product.
https://www.deadlinkchecker.com
Even the pricing models seem similar.
For example, it marks a URL that's a form action destination (https://bernard.app/demo) as a 404. I technically should return 405 on GET, but it is a false positive nonetheless, because never affects any user.
My suggestion is to try both and see what works better for your website.