Repository navigation
Soundcheck Page Health
The health module checks that every page loads and renders without errors. This page lists everything it reports, the level of each issue and how it becomes a change in a comparison.
Every issue has a check ID, such as health/php-error, shown in the HTML report and the JSON. In a comparison, a new error is BROKE and a new warning or notice is NEW; see Reading results.
| Check | Level | Title | When |
|---|---|---|---|
health/unreachable |
error | Page didn't load: <reason> |
the browser couldn't load the page at all, e.g. net::ERR_CONNECTION_REFUSED or timed out
|
health/status |
error | Server error 500 |
the page answered 5xx |
health/status |
error | Page not found (404) |
the page answered 404 or 410 |
health/blocked |
error | Blocked by a bot challenge (<vendor>) |
a firewall or bot check answered instead of the site: Cloudflare, Sucuri, Akamai, Imperva, DataDome, AWS WAF, or a 403 or 429 that asks for a CAPTCHA |
health/auth-required |
error | Password required (401) |
a 401 with a WWW-Authenticate header; the detail reads The server asks for a login: WWW-Authenticate: Basic realm="Staging"
|
health/status |
error | Page blocked (403) |
any other 4xx |
health/redirect-chain |
notice | Redirects 3 times before loading |
more than two redirects; the detail lists each hop |
A page that answered 200 in the snapshot and 404 now is the classic BROKE:
[BROKE] Page not found (404) - /spring-offer/
fix: Restore the page, or redirect it and update links to it.
Pages that time out get Page didn't load: timed out. The limit is --timeout per page (default 30 s).
A firewall challenge or a password prompt gets that one issue and nothing else from the firewall's page. In a comparison, a site that starts challenging the runner shows one BROKE line. The changes that only come from the challenge page (a changed title, lost text, visual changes) are dropped. The fix says how to get past it: allowlist the runner's IP address, or pass a header the firewall lets through with -H "Name: value"; for a password, -H "Authorization: Basic <base64 of user:password>".
When PHP prints an error into the page (display_errors on, as on many staging sites), Soundcheck reads it from the rendered text, with the file and line:
| Check | Level | PHP message |
|---|---|---|
health/php-error |
error |
Fatal error, Parse error, Recoverable fatal error
|
health/php-error |
warning | Warning |
health/php-error |
notice |
Notice, Deprecated, Strict Standards
|
[NEW] PHP Warning: Undefined array key "layout" - /, /about/, /blog/ +33 more
source: themes/acme/footer.php:42
fix: Fix the code at the source line and turn display_errors off in production so visitors never see these.
The source is shortened to the part you recognize: /var/www/html/wp-content/themes/acme/footer.php becomes themes/acme/footer.php. Paths under wp-includes/, wp-admin/, vendor/, app/, src/ or public/ keep that folder. Long messages are cut at 140 characters so the same error groups across pages and runs. Up to 20 PHP errors are read per page.
Tip
On staging, turn display_errors on (or WP_DEBUG and WP_DEBUG_DISPLAY) while you snapshot and compare. Errors that PHP only logs are invisible from the browser; the module still catches their effects (a white screen, the critical error screen, a 500).
Only the first of these that matches is reported:
| Check | Level | Title | Detects |
|---|---|---|---|
health/wp-critical |
error | WordPress "critical error" screen |
"There has been a critical error on this website" |
health/database |
error | Database error on the page |
"Error establishing a database connection", SQLSTATE[…]
|
health/error-page |
error | Application error page |
Laravel and Symfony error pages, Ignition, a stack trace, an uncaught exception |
health/white-screen |
error | Blank page (white screen) |
status 200 with no visible text and nothing loaded but a favicon (WordPress sends even a blank page's favicon request through a redirect, which doesn't count) |
The fix for the critical error screen points at the email WordPress sends to the admin and at wp-content/debug.log with WP_DEBUG_LOG on.
Uncaught exceptions, console.error() calls and errors the browser logs (a blocked script, a CSP violation) are recorded during the desktop load and the mobile reload.
| Check | Level | Title |
|---|---|---|
health/js-error |
warning | JS error: <message> |
[NEW] JS error: Uncaught TypeError: Cannot read properties of undefined (reading 'init') - /shop/
source: https://staging.yoursite.com/wp-content/plugins/store-locator/js/locator.js:1
fix: Open the browser console on this page to see the full stack.
The source is the script and line, without its query string (?ver=2.0), so a plugin's error groups across pages. Numbers in the message are replaced with N so timestamps and IDs don't split one error into many. An error in a <script> inside the page has the source inline script, since its line moves with the content. Errors caused by clicking are reported by the interact module instead.
Every request the page makes is watched.
| Check | Level | Title | When |
|---|---|---|---|
health/failed-request |
error or warning | POST /wp-admin/admin-ajax.php returned 500 |
an XHR or fetch call, or any request to admin-ajax.php or /wp-json/, answered 4xx or 5xx; 5xx and 403 are errors, the rest warnings |
health/failed-request |
warning | Request failed (ERR_NAME_NOT_RESOLVED): <url> |
a request got no answer at all |
health/broken-asset |
warning | Broken image: /wp-content/uploads/team.jpg (404) |
an image, script, stylesheet or font answered 4xx or 5xx |
health/favicon |
notice | No favicon |
/favicon.ico is missing; one site-level issue listing the pages that asked for it, keyed by the path, so a changing ?v=2 doesn't count |
Same-site URLs are shown as paths; other hosts in full.
At the end of a run, Soundcheck tests links on the visited pages that point at same-site pages it didn't visit (grouped pages, pages past the limit or outside --include), with a HEAD request, falling back to GET when the server doesn't support HEAD. Each link is requested exactly as written, trailing slash and all. It tests up to 300 such links, four at a time and at the run's pace, with a progress line (Checking 3 links: 1/3 in plain mode). Pages it did visit use the status it saw.
| Check | Level | Title |
|---|---|---|
health/broken-link |
warning | Broken link to /spring-offer/ (404) |
A link answering 404, 410 or 5xx is reported once, with every page that links to it. Links that match discovery's built-in excludes or your pages.exclude (add to cart, log out, /wp-admin and so on) are never requested. Neither are files (images, PDFs, CSS, JS).
| Check | Level | Title | When |
|---|---|---|---|
health/mixed-content |
warning | Insecure (http://) resource on an https page |
an https page loads something over http://; the fix suggests wp search-replace
|
health/no-title |
notice | Page has no <title> |
a 200 page without a title |
health/noindex |
notice | Page is set to noindex |
noindex in a robots or googlebot meta tag, or the X-Robots-Tag header |
health/not-responsive |
notice | Page renders 980 px wide on a phone |
the phone screenshot is wider than 500 px: no viewport meta tag, or a fixed-width element |
A noindex that appears after a change is worth a look: WordPress adds one to every page when "Discourage search engines" is ticked, a common accident when a staging database is copied to production.
[NEW] Page is set to noindex - /services/
noindex, nofollow
fix: If this page should be in search results, remove the noindex (WordPress: Settings → Reading, or your SEO plugin).
In a comparison, the health module also reports, as CHANGED:
| Check | Title | When |
|---|---|---|
health/title |
Page title changed |
the <title> differs; before → after shows both |
health/content-lost |
Page lost most of its text |
a page that had more than 400 characters of visible text now has less than half |
health/redirect |
Page now ends somewhere else |
the page redirects to a different place than before |
[CHANGED] Page title changed - /about/
About – Acme Studio → About us – Acme Studio
Pages that fail (4xx, 5xx, didn't load) are left out of these, since their status is already reported.
← Discovery · Home · Visual changes →
rhythm · Useful ideas. Working systems. · Check only sites you own or have permission to test. Keep staging sealed (responsible use) · This wiki is published from wiki/ in the rhythm repository; suggest changes there with a pull request.
Get started
rhythm in depth
Tuner
- Overview
- Your first scan
- Reading results
- Choosing checks
- Requests and politeness
- Output and reports
- Batch scans
- Local scans
- Integrity and malware
- Changes and notifications
Tuner in depth
- Known vulnerabilities
- PHP advisories
- CI and code scanning
- GitHub Action
- Config file
- Watch and serve
- Writing checks
- Check packs
- Finding reference
- CLI reference
- Coming from Wordfence CLI or WPScan
- FAQ and troubleshooting
Soundcheck
- Overview
- Your first check
- Reading results
- Review and accept
- Discovery
- Page health
- Visual changes
- Interactions
- Forms and flows
- Cache
- Speed
- Accessibility and SEO
Soundcheck in depth
- CI
- CI baselines and PR comments
- Configuration
- Error rules and exec
- WordPress updates
- Fleet and client reports
- History
- Import from BackstopJS and Lost Pixel
- Firefox and WebKit
- CLI reference
- FAQ and troubleshooting
- Benchmark
Echo
- Overview
- Your first run
- Connectors
- Sealing staging
- Echo Inbox
- Messages and checks
- Checks and rules
- Attachments
- Actions and compare
- With Soundcheck
- Replay
- Deliverability
Echo in depth
- Docker and DDEV
- Agency hub and reports
- Schedule
- MCP server
- Conformance kit
- CI
- Configuration
- API
- CLI reference
- FAQ and troubleshooting
- Benchmark
Project