Probe Site Doctor
Probe Site Doctor analyzes a WordPress site across performance, database, configuration, plugin/theme, technical SEO and developer checks, and produces an actionable WordPress website health, performance and diagnostics reports.
by Djouonang Landry · github.com/landrydjouonang-hue/probe-site-doctor
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/landrydjouonang-hue/probe-site-doctor/archive/refs/heads/main.zip
Probe Site Doctor
WordPress health, performance & developer diagnostics.
49 read-only checks · printable health report · per-page speed causes · Core Web Vitals from real visitors
Probe Site Doctor analyzes a WordPress site across performance, database, configuration, plugin/theme, technical SEO and developer checks, and produces an actionable health report: a 0–100 score, findings ranked by severity, and a concrete recommendation for each finding. It then tells you what is making a given page slow, and — if you switch it on — what real visitors' browsers measured.
Everything it does is read-only. It never changes your site, never installs an update, never deletes a row, and never contacts a third party.
Author: Djouonang Landry · License: GPL-2.0-or-later · Requires WordPress 6.4+, PHP 8.0+
Scope — what it is and what it is not
| It is | It is not |
|---|---|
| A diagnostic tool for performance, database health, configuration, maintenance status and technical SEO settings | A malware scanner, file-integrity monitor, firewall or vulnerability database |
| Read-only: a scan never changes the site | A replacement for a dedicated security product |
| Includes a few basic hardening checks (e.g. debug output, public debug logs, outdated PHP) | Proof that a site is secure. A high score does not mean that. |
This scope statement is shown on the dashboard itself.
Features (v0.9.0)
- Modular diagnostic engine with a public check interface and registration hooks
- Four severity levels: Good, Information, Warning, Critical
- Six categories: Performance, Database, Configuration, Plugins & Themes, SEO (Technical), Developer
- Stored scan history with automatic retention (20 completed scans by default)
- Dashboard: health score, score trend, per-area scores, severity filter, per-finding details
- Website Health Report: a printable report with an overall diagnostic summary, priority recommendations and six sections (Performance, Database, Plugins & Themes, Configuration, Security Indicators, SEO). Generated on demand from any stored scan, printable straight to PDF, and downloadable as one self-contained HTML file
- Scans run one check per REST request, with a progress bar and no PHP timeouts. The form falls back to a single request when JavaScript is off.
- REST API (
probe-site-doctor/v1), custom capabilities, multisite-aware uninstall - 49 checks: performance (Phase 2), database (Phase 3), plugin/theme/version (Phase 4), configuration indicators (Phase 5), technical SEO (Phase 6) and developer diagnostics (Phase 8)
- Developer screen: the full system report — environment, active theme, active plugins, PHP extensions, PHP and WordPress memory limits, cron, REST availability, debug settings, database details and defined constants — with a copy/download diagnostic report (Markdown or JSON) for support tickets
- Page Speed screen: analyse any page on the site and get a prioritised list of what is making it slow, with a concrete fix for each cause
- Core Web Vitals from real visitors (optional, off by default): LCP, INP, CLS, FCP and TTFB collected first-party from visitors' browsers, summarised as 75th percentiles per page and device
- Prioritised update recommendations for WordPress, plugins, themes and PHP. Site Doctor never installs updates and never contacts WordPress.org; it reads the update data WordPress already has.
- Database findings show Finding · Estimated impact · Recommended action · Optional manual cleanup. Nothing is ever deleted automatically; see docs/CLEANUP-POLICY.md
Built-in checks
"How" shows how each check gets its evidence. Server means it reads settings, files, the database or PHP state. Loopback means the server requests its own public homepage as an anonymous visitor and analyses the response.
| Category | Check | ID | How |
|---|---|---|---|
| Performance | Page cache (cache signals + server response time) | performance.page-cache |
Loopback |
| Performance | Persistent object cache | performance.object-cache |
Server |
| Performance | PHP OPcache | performance.opcache |
Server |
| Performance | Large images (media library) | performance.large-images |
Server |
| Performance | Image optimization indicators | performance.image-optimization |
Server |
| Performance | Script and style loading (render-blocking) | performance.render-blocking-assets |
Loopback |
| Performance | Excessive asset loading | performance.asset-weight |
Loopback |
| Database | Query indicators (round trip, typical query, SAVEQUERIES) | database.query-performance |
Server |
| Database | Database size (and growth since previous scan) | database.size |
Server |
| Database | Large tables (log/session/stats detection) | database.large-tables |
Server |
| Database | Table storage engines | database.tables |
Server |
| Database | Autoloaded options | database.autoloaded-options |
Server |
| Database | Post revisions | database.revisions |
Server |
| Database | Transients | database.transients |
Server |
| Database | Post meta volume | database.postmeta |
Server |
| Database | Orphaned metadata indicators | database.orphaned-meta |
Server |
| Database | Trash, spam and auto-drafts | database.bloat |
Server |
| Configuration | WordPress core version (and automatic security releases) | configuration.wordpress-version |
Server |
| Configuration | File editing settings | configuration.file-editing |
Server |
| Configuration | HTTPS configuration | configuration.https |
Loopback |
| Configuration | Administrator account indicators | configuration.admin-accounts |
Server |
| Configuration | XML-RPC status | configuration.xmlrpc |
Loopback |
| Configuration | REST API exposure indicators | configuration.rest-api |
Loopback |
| Configuration | WordPress version visibility | configuration.version-visibility |
Loopback |
| Configuration | Security-related configuration indicators | configuration.security-indicators |
Loopback |
| Configuration | PHP version (support and speed) | configuration.php-version |
Server |
| Configuration | Debug settings | configuration.debug-mode |
Server |
| Plugins & Themes | Outdated plugins | extensions.outdated-plugins |
Server |
| Plugins & Themes | Outdated themes | extensions.outdated-themes |
Server |
| Plugins & Themes | Inactive plugins | extensions.inactive-plugins |
Server |
| Plugins & Themes | Inactive themes | extensions.inactive-themes |
Server |
| Plugins & Themes | Plugin compatibility indicators | extensions.compatibility |
Server |
| SEO | Search engine visibility | seo.search-visibility |
Server |
| SEO | Permalink structure | seo.permalink-structure |
Server |
| SEO | Indexing configuration indicators | seo.indexing |
Loopback |
| SEO | Sitemap availability | seo.sitemap |
Loopback |
| SEO | Page titles | seo.page-titles |
Loopback |
| SEO | Meta descriptions | seo.meta-descriptions |
Loopback |
| SEO | Image alt text | seo.image-alt-text |
Loopback |
| SEO | Broken internal links | seo.internal-links |
Loopback |
| Developer | Environment information | developer.environment |
Server |
| Developer | Active theme and plugins | developer.active-components |
Server |
| Developer | PHP extensions | developer.php-extensions |
Server |
| Developer | PHP memory limit | developer.memory-limit |
Server |
| Developer | WordPress memory limit | developer.wp-memory-limit |
Server |
| Developer | Cron status | developer.cron |
Server |
| Developer | REST API status | developer.rest-api |
Loopback |
| Developer | Debug log | developer.debug-log |
Server |
| Developer | Database configuration | developer.database-configuration |
Server |
The WordPress and PHP version checks are listed under Configuration because they cover support status as well as speed. Both include performance guidance.
Three kinds of performance data, kept apart
| Source | What it tells you | Where |
|---|---|---|
| Server-side analysis (always available) | What the server sends and the work the page gives a browser: response time, HTML and asset weight, render-blocking files, images, third-party domains, caching and compression headers. This is where the causes are, and every cause comes with a fix. | Scan findings, Page Speed screen |
| Field data from real browsers (optional, off by default) | What visitors actually experienced: LCP, INP, CLS, FCP, TTFB as 75th percentiles per page and device. | Page Speed screen |
| Lab tools (external, your choice) | A controlled test in Google's browser with a full audit trail. Site Doctor links to PageSpeed Insights for the page you are looking at; it never sends your URLs anywhere itself. | Link on the Page Speed screen |
A server-side number is never presented as a page-load time, and the field data section says plainly that it needs traffic before it means anything.
Server-side diagnostics vs. frontend performance
The scan itself runs on the web server, so its findings do not measure browser page-load performance. Scan findings never report LCP, INP or CLS — those come only from the field-data collector described below — and their figures are not page-load times:
- Server response time (page cache check) is the time the server takes to answer a request from itself. It excludes visitor network latency, image and script downloads, and rendering.
- Asset checks read the homepage HTML. They count files, identify render-blocking markup, and add up the on-disk size of local files before compression. Third-party files and assets that JavaScript loads later are not measured.
- Image checks inspect the media library. Whether a large image actually reaches visitors depends on the theme and the content.
Each finding shows a Server-side or Server loopback badge. To measure real frontend performance, use PageSpeed Insights, Lighthouse or browser developer tools.
Updates: recommended, never installed
Site Doctor reads the update data WordPress has already collected (the update_plugins, update_themes and update_core transients). It does not contact WordPress.org, does not trigger an update check, and never installs or activates anything.
Findings contribute prioritised recommendations, collected on the dashboard:
| Priority | Meaning |
|---|---|
| High | Security/maintenance release, or a patch/minor update of a component in use |
| Medium | Major version of a component in use (test on staging), or an unsupported PHP branch |
| Blocked | The update needs a newer PHP or WordPress version first |
| Low | Update for an inactive plugin or theme (deleting is usually better) |
Each finding also includes manual update steps and copyable WP-CLI commands. Only you run them.
Configuration indicators, not a security audit
The Configuration checks read settings: debug output, file editing, HTTPS, administrator accounts, XML-RPC, REST exposure, version visibility, security keys, file permissions and similar. Every such finding says so in plain words, and a clean result does not mean the site is secure.
Not checked at all: malware, modified core or plugin files, known vulnerabilities in installed code, intrusions, password strength, two-factor authentication, certificate chains and TLS versions, and whether PHP files in the uploads folder can be executed (testing that would require uploading a file). Use a dedicated security product and an SSL testing service for those.
Non-invasive by design: server-side checks read constants, options and file metadata. Probe-based checks make anonymous GET requests to the site's own endpoints (xmlrpc.php, the REST root and users endpoint, readme.html, the uploads folder). No POST, no authentication, no XML-RPC method call, nothing uploaded.
Nothing is changed: fixes appear as How to change this manually, with wp-config.php, PHP or server snippets you apply yourself. Site Doctor registers no filter that alters XML-RPC, REST or registration behaviour.
Weighting is deliberately honest: obscurity-level items (version visibility, the default wp_ table prefix, display names) are Information with Low impact and say that hiding them protects little. Only real exposure is Critical — plain HTTP in production, placeholder security keys, a world-writable wp-config.php, new registrations becoming administrators, or PHP errors shown to visitors.
Technical SEO, not an SEO platform
The SEO checks cover what a diagnostic tool can verify on the server: whether published content has titles, whether descriptions exist where a supported plugin stores them, whether internal links resolve, whether images carry alt text, whether indexing is allowed, and whether a sitemap responds.
Not covered: content quality, keyword research, rankings, search volume, backlinks, competitors, and whether pages are actually indexed (that needs Google Search Console). Site Doctor does not write meta tags or edit content — keep using Yoast, Rank Math, SEOPress or similar for that, and Site Doctor reads their per-page descriptions when they store them in post meta.
Scanning is deliberately bounded: content scans stop after a few hundred published items, and broken-link verification makes only a small number of requests. Findings say how much was examined and what was left unverified, so the numbers are never presented as a full crawl.
Developer diagnostics
Site Doctor → Developer is the screen to open when you have inherited someone else's WordPress install. It reads, live:
| Group | Contents |
|---|---|
| Environment | WordPress and PHP versions, SAPI, server software, OS, database server, URLs, environment type, locale, timezone, permalinks, paths, uploads writability, cache drop-ins |
| Active theme | Name, version, author, stylesheet/template, child and parent theme, block theme, required WP/PHP, available update |
| Active plugins | Every active plugin with version, author, available update, network scope and auto-update state — plus must-use plugins and drop-ins, the code that loads outside the plugin list |
| PHP extensions | All loaded extensions, and the status of each required and recommended one, with the image library in use |
| PHP configuration | memory_limit, execution time, input vars, upload limits, display_errors, error_log, OPcache, cURL/OpenSSL versions, disabled functions |
| WordPress memory limit | WP_MEMORY_LIMIT, WP_MAX_MEMORY_LIMIT, the limit PHP is running under, current and peak usage |
| Cron status | DISABLE_WP_CRON / ALTERNATE_WP_CRON, event and hook counts, next event, overdue events, missing core events, duplicated hooks, registered schedules |
| REST API status | The REST root and a core route requested anonymously, namespaces advertised, response time |
| Debug information | WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY, SCRIPT_DEBUG, SAVEQUERIES, fatal error handler, log path, size, last write, whether it sits in the web root |
| Database information | Server and client versions, charset and collation, prefix, table count and size, the ten largest tables, tables not on utf8mb4, max_allowed_packet, buffer pool, sql_mode |
| Defined constants | A whitelist of configuration constants |
Never collected: authentication keys and salts, the database password, and the contents of the debug log (only its size and timestamp). Only whitelisted constants are reported, and anything whose name ends in KEY, SALT, PASSWORD, SECRET or TOKEN is refused even if it is whitelisted.
Nine of these areas are also scan checks (developer.*), so they appear in the health report with a severity, an impact and manual instructions: PHP extensions, PHP memory limit, WordPress memory limit, cron, REST availability, the debug log, database configuration, the environment declaration and the active-component inventory.
Exportable diagnostic reports
The Developer screen offers the whole system report — plus the findings of the latest scan that still need attention — as:
- Copy to clipboard for pasting into a ticket or a chat,
- Download .md (
probe-site-doctor-diagnostics-<host>-<date>.md), a Markdown document with tables, - Download .json for tooling.
Both downloads are capability-checked and nonce-protected, write no file on the server and send nothing anywhere. Filters: probesd_system_info, probesd_system_report_markdown, probesd_system_report_data.
Page speed: what is making a page slow
Site Doctor → Page Speed analyses one page at a time — the home page, blog index, latest post/page/product, or any URL on this site. It fetches the page twice as an anonymous visitor (to see whether a cache answers) and reads up to three of its asset files for caching and compression headers. Then it reports:
- What the server sends: first and repeat response time, cache indicators, HTML and local asset weight, file counts, render-blocking files.
- What is making this page slow: a prioritised list of causes. Each one names what was measured, which Core Web Vital it typically affects, and how to improve it — slow server response, no page cache, no compression, render-blocking CSS/JS, heavy JavaScript, inline bloat, heavy or oversized images, missing image dimensions, a lazy-loaded hero image, no modern image formats, missing lazy loading, third-party domains without preconnect, assets served without caching headers, a large DOM, and heavy embeds.
- Heaviest files on the page and the third-party domains it references.
Limits, stated on the screen: sizes are on-disk sizes of local files before compression, third-party files are never requested, and assets that JavaScript adds later are invisible to a server-side fetch.
Core Web Vitals from real visitors
This is the only part of Site Doctor that measures what visitors experience, because it is the only part that runs in their browsers.
When an administrator enables it, a ~3 KB first-party script runs on public pages, reads the browser's own performance entries (largest-contentful-paint, layout-shift, event timing, paint timing and navigation timing) and sends them back to this site once, when the page is hidden. The Page Speed screen then shows 75th-percentile LCP, INP, CLS, FCP and TTFB — for the analysed page, for the whole site, and split by mobile and desktop viewports — each rated against Google's thresholds.
Privacy and honesty, by design:
- Off by default. It adds a script to the public site, so enabling it is a decision for the site owner (
manage_options), not for the plugin. - First-party only. Data goes to this site's own REST route. No third-party service, no CDN, no analytics vendor.
- No visitor identity. Stored rows contain a path, a device class (mobile/desktop by viewport width), a metric and a value. No IP address, no user agent, no cookie, nothing in the visitor's browser.
- Logged-in users are excluded, because their pages carry the admin bar and are rarely cached.
- Bounded. Sampling percentage and a retention window (default 30 days) are configurable; old rows are deleted automatically, and there is a one-click "delete all collected field data".
- The collector route only exists while it is enabled, accepts same-origin requests only, validates and range-checks every value, and is rate limited per address.
- Field data needs traffic. Fewer than about 30 samples is an indication, not a measurement — and the screen says so.
Filter: probesd_web_vitals_enabled. REST: POST probe-site-doctor/v1/vitals (public while enabled), GET probe-site-doctor/v1/vitals (capability-checked).
Website Health Report
Site Doctor → Health Report turns a completed scan into a report meant to be read, printed or handed to a client. Administrators generate it on demand: the screen opens with the latest scan, and any stored scan can be selected from the picker. Scan History offers Printable report and Download on every completed scan.
The report contains:
- Header — site name and URL, scan date, generation date, who requested it, scan reference, plugin version and environment type.
- Overall diagnostic summary — score and band, a one-sentence verdict, severity counts, how many checks were scored, movement since the previous scan, how the score is calculated, and what it does not mean.
- Priority recommendations — critical findings first, then warnings, each with its section, recommended action and estimated impact.
- Section summaries — score, severity counts and a plain-language assessment per section.
- Seven sections — Performance, Database, Plugins & Themes, Configuration, Security Indicators, SEO (Technical), Developer. Each one repeats its own scope, then lists its findings with severity, explanation, estimated impact, recommended action and manual steps, followed by a compact table of checks that passed and of checks that could not be evaluated.
- Environment at scan time and a footer that repeats the score disclaimer.
The Configuration category is split for the report: the security-related indicators (debug output, file editing, HTTPS, administrator accounts, XML-RPC, REST exposure, version visibility, security keys) form the Security Indicators section, while the PHP and WordPress version checks stay under Configuration. Both the section list and the mapping are filterable: probesd_report_sections, probesd_report_section_for_check.
Printing and PDF. Print / Save as PDF prints the same document; the browser's own "Save as PDF" destination produces the PDF. Each section starts on a new page, findings and tables are kept whole where possible, and the admin menu and toolbar are excluded from the printout. No PDF engine is bundled — that would add a heavy dependency for something every browser already does.
Download. Download HTML sends one self-contained file (site-health-report-<host>-<date>-<scan>.html) with its stylesheet inline and no external references, so it can be archived, emailed or printed later. The download is capability-checked and nonce-protected, writes nothing, and leaves no file on the server. Filter: probesd_report_export_html.
The report is read-only in every sense: it renders a stored scan and never runs a check or changes a setting.
Health score
Each completed result counts as Good = 100, Warning = 50, Critical = 0, and the score is their average. Information results, failed checks and skipped checks are neutral and are left out. Bands: ≥ 80 Healthy, ≥ 50 Needs attention, below 50 Needs urgent attention. Filter: probesd_health_score.
The score is not a security guarantee and not a certification. It summarises only the checks in the report: Site Doctor does not scan for malware, modified files, known vulnerabilities or intrusions, and it cannot measure what visitors experience in a browser. A high score means these checks passed, nothing more. That sentence (Score::disclaimer()) is printed next to the score on the dashboard, in the report summary and in the report footer.
Capabilities
| Capability | Grants | Default |
|---|---|---|
probesd_view_reports |
Dashboard, history, read reports, generate and download the health report, Developer and Page Speed screens, export diagnostics | Administrator |
probesd_run_scans |
Start scans, delete reports | Administrator |
probesd_run_cleanup |
Confirm future cleanup operations (none exist yet; typed confirmation always required) | Administrator |
Enabling the field-data collector, changing its sampling/retention or deleting the collected data requires manage_options, because it changes what runs on the public site.
Extending
See docs/WRITING-CHECKS.md. In short:
add_action( 'probesd_register_checks', function ( $registry ) {
$registry->register( new My_Plugin\Checks\My_Check() );
} );
More docs: docs/ARCHITECTURE.md, docs/DATABASE.md, docs/CLEANUP-POLICY.md.