Website Bug Finder Tool: Find Every Bug Before Users Do
A website bug finder tool catches JS errors, broken links, layout shifts and mixed content automatically. Real test data plus a free Python bug finder script.
A website bug finder tool loads your pages in a real browser and records every defect a visitor would hit: uncaught JavaScript exceptions, console errors, failed network requests, 4xx/5xx sub-resources, broken links, mixed content, layout shifts, accessibility violations and hydration mismatches. A good one also filters out false positives so you only fix bugs that are actually real.
That last part matters more than most buyers realise. The bug-tracking tools that dominate search results for "website bug finder" are mostly feedback widgets: a human clicks on a broken element and the tool captures a screenshot. They are excellent at reporting bugs, but they find nothing on their own.
This guide covers the automated side: what classes of bugs a machine can find, how to run a bug finder yourself in under 80 lines of Python, real output from three production sites, and the false positives that make most "site checker" reports untrustworthy.
What a Website Bug Finder Actually Detects
An automated website bug finder detects defects that leave a measurable trace in the browser: an exception, an error status code, a failed request, a layout shift entry, or a DOM rule violation. It cannot judge whether your copy is persuasive or your pricing is confusing โ those need humans.
Here is the full taxonomy, split by how each bug is caught:
| Bug class | Browser signal | Typical cause | User impact |
|---|---|---|---|
| Uncaught JS exception | pageerror event | Null reference, failed third-party script | Buttons, forms or menus stop working |
| Console error | console.error | Failed fetch, CSP violation, deprecated API | Often silent; sometimes a broken feature |
| Failed request | requestfailed | DNS failure, blocked host, TLS error | Missing images, fonts or scripts |
| HTTP 4xx/5xx sub-resource | Response status โฅ 400 | Deleted asset, wrong path after deploy | Broken image, unstyled page |
| Broken link | Link target returns โฅ 400 | Moved page, typo, dead external site | Dead end, lost crawl equity |
| Mixed content | http:// request on an https:// page | Hardcoded asset URL | Blocked script/iframe, "Not secure" warning |
| Layout shift | layout-shift PerformanceObserver entry | Image without dimensions, late ad/banner | Mis-clicks, failed CLS |
| Hydration mismatch | React errors #418 / #423 / #425 | Server/client render differences | Content flicker, full client re-render |
| Accessibility violation | axe-core rule failure | Missing labels, low contrast | Unusable for screen readers, legal risk |
๐ก Rule of thumb: If a bug only appears after JavaScript runs, a tool that fetches raw HTML with
curlorrequestswill never see it. Modern bug finding requires a headless browser such as Chromium.
Feedback Widgets vs Automated Bug Finders
These two categories share a name in search results but solve different problems. Mixing them up is the most common reason teams buy the wrong tool.
| Dimension | Visual feedback / bug tracker | Automated website bug finder |
|---|---|---|
| Who finds the bug | A human tester or client | The tool, by loading pages |
| Finds bugs nobody noticed | No | Yes |
| Captures console + network context | Yes, at the moment of the report | Yes, on every page it loads |
| Judges design and copy | Yes | No |
| Scales to hundreds of pages | Only with hundreds of testers | Yes, via a crawl |
| Best moment to use | UAT and client review | Before launch, after every deploy, on a schedule |
The two work well together. Run the automated finder first so human testers are not wasting their time reporting a 404 image or a console exception that a machine could have caught in seconds.
Build a Website Bug Finder in Python (Runnable Script)
You can build a basic but honest bug finder with Playwright and httpx. The script below loads one page in headless Chromium, listens to the browser's error channels, measures cumulative layout shift with the Layout Instability API, then validates every link on the page.
"""
find_bugs.py โ a minimal single-page website bug finder.
Loads a page in headless Chromium and reports the bug classes a human
tester usually misses: uncaught JS exceptions, console errors, failed
network requests, HTTP 4xx/5xx sub-resources, mixed content, layout
shifts, and broken links on the page.
Usage:
pip install playwright httpx && playwright install chromium
python3 find_bugs.py https://example.com
"""
import asyncio
import sys
from urllib.parse import urljoin, urlparse
import httpx
from playwright.async_api import async_playwright
CLS_PROBE = """
window.__shifts = [];
new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
if (!e.hadRecentInput) window.__shifts.push(e.value);
}
}).observe({ type: 'layout-shift', buffered: true });
"""
async def check_links(urls: list[str]) -> list[tuple[str, int | str]]:
broken = []
headers = {"User-Agent": "Mozilla/5.0 (bug-finder script)"}
async with httpx.AsyncClient(follow_redirects=True, timeout=10, headers=headers) as client:
sem = asyncio.Semaphore(8)
async def probe(u: str):
async with sem:
try:
r = await client.head(u)
if r.status_code >= 400: # many servers mis-answer HEAD: confirm with GET
r = await client.get(u)
# 429 = rate limited and 403 = bot wall: not proof of a broken link
if r.status_code >= 400 and r.status_code not in (403, 429):
broken.append((u, r.status_code))
except httpx.HTTPError as exc:
broken.append((u, type(exc).__name__))
await asyncio.gather(*(probe(u) for u in urls))
return broken
async def main(url: str) -> None:
findings: dict[str, list[str]] = {
"js_exceptions": [], "console_errors": [], "failed_requests": [],
"http_errors": [], "mixed_content": [],
}
page_is_https = urlparse(url).scheme == "https"
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.add_init_script(CLS_PROBE)
page.on("pageerror", lambda e: findings["js_exceptions"].append(str(e)[:160]))
page.on("console", lambda m: m.type == "error"
and findings["console_errors"].append(m.text[:160]))
# ERR_ABORTED = the browser cancelled the request itself (analytics beacons,
# superseded fetches) โ not a failure the visitor ever sees.
page.on("requestfailed", lambda r: r.failure != "net::ERR_ABORTED"
and findings["failed_requests"].append(f"{r.failure} {r.url[:120]}"))
def on_response(resp):
if resp.status >= 400:
findings["http_errors"].append(f"{resp.status} {resp.url[:120]}")
if page_is_https and resp.url.startswith("http://"):
findings["mixed_content"].append(resp.url[:120])
page.on("response", on_response)
await page.goto(url, wait_until="load", timeout=45_000)
await page.wait_for_timeout(2_000) # let late scripts and shifts settle
cls = await page.evaluate("window.__shifts.reduce((a, b) => a + b, 0)")
hrefs = await page.eval_on_selector_all(
"a[href]", "els => els.map(e => e.getAttribute('href'))")
await browser.close()
links = sorted({urljoin(url, h).split("#")[0] for h in hrefs
if h and not h.startswith(("mailto:", "tel:", "javascript:", "#"))})
broken = await check_links(links)
print(f"Bug report for {url}\n")
for name, items in findings.items():
print(f"{name:16} {len(items)}")
for item in items[:5]:
print(f" - {item}")
print(f"{'layout_shift':16} CLS {cls:.3f} ({'good' if cls <= 0.1 else 'needs work'})")
print(f"{'broken_links':16} {len(broken)} of {len(links)} checked")
for link, status in broken[:10]:
print(f" - {status} {link}")
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit("Usage: python3 find_bugs.py <URL>")
asyncio.run(main(sys.argv[1]))Two design choices in this script are deliberate. Both came from running it against real sites, and both are explained in the next section.
- A link that fails a
HEADrequest is re-checked withGETbefore it is called broken. net::ERR_ABORTEDrequest failures are ignored, because the browser cancelled those requests itself.
Real Results: Running the Bug Finder on 3 Production Sites
We ran the script on 1 October 2026 against three public homepages: gov.uk (a well-known reference for clean engineering), wordpress.org (a large, busy site), and bugviso.com (our own homepage).
The first version of the script used HEAD for every link and counted every failed request. Here is what it reported, followed by the corrected version:
| Site | Links checked | Naive script: bugs reported | Corrected script: bugs reported | CLS |
|---|---|---|---|---|
| gov.uk | 57 | 0 | 0 | 0.000 |
| wordpress.org | 51 | 3 | 0 | 0.009 |
| bugviso.com | 16 | 0 | 0 | 0.014 |
All three sites came back clean. The interesting result is the 3 bugs on wordpress.org that turned out not to be bugs.
False Positive 1: A Link That Answers HEAD With 404
The naive script flagged a social profile link on wordpress.org as broken. Checking it by hand shows why:
curl -sI -o /dev/null -w "HEAD %{http_code}\n" https://bsky.app/profile/wordpress.org
HEAD 404
curl -s -o /dev/null -w "GET %{http_code}\n" https://bsky.app/profile/wordpress.org
GET 200The page exists. The server simply answers HEAD differently from GET, which is common with JavaScript-rendered apps and some CDNs. A broken link checker that trusts HEAD alone will flag a page that works perfectly for every human visitor.
False Positives 2 and 3: Aborted Analytics Beacons
The naive script also reported two net::ERR_ABORTED failures for requests to an analytics collection endpoint. These requests were cancelled by the browser itself when the test closed the page.
A visitor never sees them, and nothing on the page is broken. Counting them as bugs would put noise at the top of every report for any site running analytics.
โ ๏ธ Why this matters: A bug finder that reports 3 bugs on a clean page trains your team to ignore its reports. Precision is a feature. Ask any vendor how they handle
HEAD/GETdisagreement, rate-limited429responses, and bot-wall403responses on external links.
The 6 Bug Classes That Slip Past Manual Testing
Manual QA catches visual problems well. It reliably misses the categories below, because they are invisible on a fast laptop with a warm cache.
1. Console Errors That Do Not Break the Page (Yet)
A failing analytics call or a deprecated API warning rarely changes what the tester sees. But JavaScript console errors can break SEO when they stop rendering before key content is inserted, and Googlebot renders pages with a real Chromium engine.
// โ Broken: throws when the API returns no "features" array, killing the render
const items = data.features.map((f) => f.title)// โ
Fixed: defensive default keeps the rest of the page rendering
const items = (data?.features ?? []).map((f) => f.title)2. Hydration Mismatches
React logs minified errors #418, #423 and #425 when server-rendered HTML does not match the client render. The page usually looks fine after the client takes over, so testers miss it. Search engines and users still pay for the flicker and the extra work. Our guide to React hydration errors and SEO covers the common causes, such as dates, window checks and random IDs.
3. Layout Shifts on Slow Connections
An image without width and height attributes shifts nothing on a fast connection, because it loads before first paint. On a throttled mobile connection it arrives late and pushes content down. CLS must stay at or below 0.1 to pass Google's Core Web Vitals threshold.
<!-- โ Broken: browser reserves 0px until the file downloads -->
<img src="/hero.webp" alt="Dashboard screenshot">
<!-- โ
Fixed: aspect ratio is reserved before the image arrives -->
<img src="/hero.webp" alt="Dashboard screenshot" width="1200" height="675">4. Mixed Content After an HTTPS Migration
One hardcoded http:// script URL inside a CMS block is enough. Browsers block active mixed content such as scripts and iframes outright, so the feature silently disappears for every visitor.
5. Broken Links Deep in the Site
Link rot accumulates on pages nobody revisits: old blog posts, footers, documentation. Manual testers follow the happy path. A crawler follows every href.
6. Tap Targets and Accessibility Rules
Controls smaller than 24ร24 CSS pixels fail WCAG 2.5.8 Target Size (Minimum). Missing form labels and low-contrast text fail axe-core rules. None of these look "broken" to a sighted tester using a mouse.
Case Study: Finding (and Fixing) Bugs on Our Own Site
We run BugViso on bugviso.com, and in September 2026 the result was not flattering. The first full audit of the redesigned site scored 19/100 (F).
Working through the report showed two very different kinds of problem:
| Finding type | Examples | Action taken |
|---|---|---|
| Real site bugs | Analytics firing before cookie consent, missing CSP and security headers, homepage served as an empty SPA shell to non-JS crawlers, hero image paint cost, third-party font loading | Fixed in the site: consent-gated analytics, CSP and headers, prerendered homepage, self-hosted fonts |
| Scanner false positives | Soft-404 detection matching on page title alone, CORS checks on simple requests, overlap checks ignoring layering, duplicate checks ignoring canonical/noindex | Fixed in the scanner |
| Real but small | Roughly 92 KB of unused JavaScript and CSS (mostly framework runtime and a global stylesheet) | Accepted for now and documented |
After both sets of fixes, a local re-scan scored 86/100. The remaining gap was mostly HTTPS and HSTS checks that cannot pass on a local server, which put the projected production score at about 98.
We then re-calibrated the scanner against reference sites. gov.uk moved from 82 to 98 and wordpress.org from 40 to 71 once more false-positive classes were removed. These included rate-limited 429 responses counted as broken links, alt="" on decorative images flagged as missing alt text, and CSS-sized images flagged for missing dimensions.
๐ก Takeaway: A low score is only useful if every finding is real. When you evaluate a website bug finder tool, run it on a site you know is clean. If it reports dozens of problems on gov.uk, the problem is the tool.
How BugViso Finds Website Bugs Automatically
BugViso runs the same browser-level checks as the script above, across a multi-page crawl and with many more engines. Every scan loads your site in headless Chromium through Playwright, so JavaScript-rendered content, client-side routing and injected links are all tested as a visitor would see them.
On each scan, the bug-finding engines cover:
- Console Health & JS Diagnostics: uncaught exceptions with message, source file, line number and a short stack trace, plus console errors and warnings.
- Network Health & Asset Integrity: failed requests, mixed content, and concurrent validation of internal and external links and image sources against 404s, redirect loops and server errors.
- React Hydration Engine: detects SSR hydration mismatches (minified codes #418/#423/#425 and their unminified text signatures) for React, Next.js and Vue.
- Visual & Layout QA: clipped text, overlapping elements, and OpenCV SSIM visual regression against an approved baseline.
- Accessibility and mobile: a WCAG 2.1 A/AA audit with self-hosted axe-core, plus a mobile device-emulation pass that checks viewport, horizontal overflow and tap targets.
- Multi-page crawl: sitemap-aware discovery with a rendered-DOM link crawl, so bugs on page 40 are found as reliably as bugs on the homepage.
Findings are ranked by impact in a prioritised remediation playbook. AI-written code fixes for your exact markup land in the branded PDF, so a developer gets the fix alongside the bug. The full list of checks is on the developer code and visual QA feature page.
Traps and Edge Cases When Hunting Website Bugs
Bot walls return 403 to every scanner. Cloudflare, Akamai and similar services often challenge headless browsers. A bug finder should recognise a challenge page and report "protected site" rather than "every link is broken." BugViso detects protected sites and tells you instead of guessing.
Rate limiting looks like breakage. Checking 200 external links in parallel can trigger 429 Too Many Requests. Treat 429 as "unknown," cap concurrency, and retry later.
Cookie banners hide the real page. If a scanner clicks "Accept," it tests a consented page that a first-time visitor never sees. Pre-consent behaviour is exactly where privacy bugs hide, such as tracking cookies set before consent.
Single-sample timing is noisy. One slow LCP or TTFB measurement on a shared network is not a regression. Re-measure before you file a performance bug.
Staging is not production. CDN rules, security headers and redirects frequently differ between environments. Always run a final pass against the production URL after deploy.
Frequently Asked Questions
What is the best website bug finder tool?
The best website bug finder tool renders pages in a real browser, crawls beyond the homepage, and keeps false positives low. Check for headless-browser rendering, console and network capture, link validation that confirms with GET, accessibility rules, and a prioritised report. Test any candidate on a site you know is clean.
Can a website bug finder replace manual QA testing?
No. Automated bug finders catch measurable defects (errors, broken links, layout shifts, accessibility rule failures) far faster than people. Humans are still needed for usability, copy, design intent and complex multi-step flows such as checkout.
How do I find bugs on a website for free?
Open Chrome DevTools, check the Console and Network tabs for red entries, and run the Python script in this guide against each important page. For a multi-page check with a report, a free BugViso scan covers the same checks across a crawl without any setup.
Why does my bug finder report broken links that work in my browser?
Usually because the server answers HEAD requests differently from GET, blocks automated user agents with 403, or rate-limits with 429. Re-test the link with a GET request and a browser user agent before you treat it as broken.
How often should I scan my website for bugs?
Run a scan after every production deploy and on a weekly or monthly schedule in between. Link rot, expiring certificates and third-party script changes introduce bugs without any change on your side. See why to run a site audit on a schedule.
Conclusion
The bugs that cost you visitors are the ones nobody on your team sees: console errors, broken links on old pages, layout shifts on slow phones, and mixed content after a migration. Finding them reliably takes a real browser, a crawl and a low false-positive rate, which is exactly what a free BugViso scan runs on your site in about two minutes.
See where your site stands
Run a free BugViso audit for SEO, speed, accessibility and AI search readiness โ with fixes you can ship today.