{"id":316836,"date":"2026-06-24T08:34:50","date_gmt":"2026-06-24T08:34:50","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/tidy-broken-link-scan\/"},"modified":"2026-06-24T15:16:08","modified_gmt":"2026-06-24T15:16:08","slug":"tidy-broken-link-scan","status":"publish","type":"plugin","link":"https:\/\/br.wordpress.org\/plugins\/tidy-broken-link-scan\/","author":23467890,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.5.0","stable_tag":"2.5.0","tested":"7.0","requires":"6.0","requires_php":"8.2","requires_plugins":null,"header_name":"Tidy Broken Link Scan","header_author":"Pierre","header_description":"Scans posts, pages and menus for broken links and images, using the WordPress HTTP API. Live progress, ignore list.","assets_banners_color":"7a39c6","last_updated":"2026-06-24 15:16:08","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/tidy-broken-link-scan\/","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":38,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"2.5.0":{"tag":"2.5.0","author":"pierreferrolliet","date":"2026-06-24 15:16:08"}},"upgrade_notice":{"2.4.1":"<p>Code cleanup: all licence checks and premium-feature gating removed from the free plugin.<\/p>","2.4.0":"<p>New features: broken anchor detection, redirect chain tracking, Slack notifications, scan diff, and health score dashboard.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3584291,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3584291,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3584291,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3584291,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3584291,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250},"banner.svg":{"filename":"banner.svg","revision":3584291,"resolution":false,"location":"assets","locale":false}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["2.5.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3584894,"resolution":"1","location":"assets","locale":"","width":1600,"height":984}},"screenshots":{"1":"Scan dashboard with health score and link results"}},"plugin_section":[262246],"plugin_tags":[905,14279,40482,163,14280],"plugin_category":[50,55],"plugin_contributors":[259182],"plugin_business_model":[],"class_list":["post-316836","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-905","plugin_tags-broken-links","plugin_tags-dead-links","plugin_tags-images","plugin_tags-link-checker","plugin_category-media","plugin_category-seo-and-marketing","plugin_contributors-pierreferrolliet","plugin_committers-pierreferrolliet"],"banners":{"banner":"https:\/\/ps.w.org\/tidy-broken-link-scan\/assets\/banner-772x250.png?rev=3584291","banner_2x":"https:\/\/ps.w.org\/tidy-broken-link-scan\/assets\/banner-1544x500.png?rev=3584291","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/tidy-broken-link-scan\/assets\/icon.svg?rev=3584291","icon":"https:\/\/ps.w.org\/tidy-broken-link-scan\/assets\/icon.svg?rev=3584291","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/tidy-broken-link-scan\/assets\/screenshot-1.png?rev=3584894","caption":"Scan dashboard with health score and link results"}],"raw_content":"<!--section=description-->\n<p>BLC scans every published post, page and public custom post type on your WordPress site for broken links and images. It checks <code>&lt;a href&gt;<\/code>, <code>&lt;img src&gt;<\/code>, <code>&lt;img srcset&gt;<\/code>, and <code>&lt;source srcset&gt;<\/code> \u2014 all natively, using the WordPress HTTP API. No SaaS, no API key, no external dependency.<\/p>\n\n<p><strong>Features:<\/strong><\/p>\n\n<ul>\n<li>Detects broken links (4xx, 5xx, timeouts)<\/li>\n<li>Checks images including <code>srcset<\/code> and <code>&lt;picture&gt;<\/code> elements<\/li>\n<li>Automatic daily scan via WP-Cron<\/li>\n<li>Instant re-scan when you save a post<\/li>\n<li>Admin dashboard with filtering (all \/ broken \/ ok)<\/li>\n<li>Direct link to edit the source post<\/li>\n<li>Works on any WordPress hosting<\/li>\n<\/ul>\n\n<p><strong>Need a full-site crawl?<\/strong> This plugin checks the links and images inside your published posts, pages and custom post types. For a complete crawl of your whole site \u2014 every URL discovered through your sitemap, plus SSL and redirect-chain checks \u2014 you can run a free scan with our companion cloud scanner at <a href=\"https:\/\/brokenlinkscan.com\">brokenlinkscan.com<\/a>. It runs in your browser, needs no installation, and is made by the same author.<\/p>\n\n<h4>More from Tidy Plugins<\/h4>\n\n<p>If you like this plugin, take a look at the rest of the Tidy suite:<\/p>\n\n<ul>\n<li><a href=\"https:\/\/wordpress.org\/plugins\/tidy-table-of-contents\/\">Tidy Table of Contents<\/a> \u2014 automatic, accessible table of contents<\/li>\n<li><a href=\"https:\/\/wordpress.org\/plugins\/tidy-draft-share\/\">Tidy Draft Share<\/a> \u2014 share drafts via secure, expiring preview links<\/li>\n<li><a href=\"https:\/\/wordpress.org\/plugins\/tidy-author-box\/\">Tidy Author Box<\/a> \u2014 author bio box with avatar and social links<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>This plugin sends HTTP requests to third-party URLs as its core functionality (broken link checking). The list of contacted URLs is dynamic: it consists of the external links and image sources found in your own published posts, pages, and custom post types.<\/p>\n\n<ul>\n<li>What is sent: HTTP HEAD (with GET fallback) requests using the WordPress HTTP API. The plugin does not transmit any user-identifying data; only the URL itself is requested.<\/li>\n<li>When: requests are issued during a scan, which is triggered manually from the admin dashboard, automatically once per day via WP-Cron, or when you save a post (re-scan of that post only).<\/li>\n<li>Why: to determine whether each link returns a 2xx\/3xx response (OK) or a 4xx\/5xx\/timeout response (broken).<\/li>\n<\/ul>\n\n<p>Because the contacted URLs are entirely determined by the content of your own site, the plugin does not rely on any single fixed third-party service. A small allow-list of well-known domains (Gravatar, gstatic, wordpress.com, Cloudflare, etc.) is skipped from scanning to avoid noise; no data is sent to those domains by the plugin itself.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>tidy-broken-link-scan<\/code> folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin in <strong>Plugins \u2192 Installed Plugins<\/strong><\/li>\n<li>Go to <strong>Liens cass\u00e9s<\/strong> in the admin menu<\/li>\n<li>Click <strong>Scanner tout le site<\/strong> to run your first scan<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"will%20it%20slow%20down%20my%20site%3F\"><h3>Will it slow down my site?<\/h3><\/dt>\n<dd><p>No. Scans run in the background via WP-Cron and are never triggered by front-end visitors.<\/p><\/dd>\n<dt id=\"does%20it%20check%20images%3F\"><h3>Does it check images?<\/h3><\/dt>\n<dd><p>Yes \u2014 <code>&lt;img src&gt;<\/code>, <code>&lt;img srcset&gt;<\/code>, and <code>&lt;source srcset&gt;<\/code> are all parsed and checked.<\/p><\/dd>\n<dt id=\"how%20often%20does%20it%20scan%3F\"><h3>How often does it scan?<\/h3><\/dt>\n<dd><p>Once per day automatically (at 03:00 server time). You can also trigger a full scan manually or re-scan individual posts on save.<\/p><\/dd>\n<dt id=\"can%20i%20scan%20a%20single%20post%3F\"><h3>Can I scan a single post?<\/h3><\/dt>\n<dd><p>Posts are automatically re-scanned every time you publish or update them.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.5.0<\/h4>\n\n<ul>\n<li>Scans now cover every public post type (posts, pages and custom post types) instead of only posts and pages \u2014 full scans, the daily auto-scan, the on-save re-scan, the post editor meta box and WP-CLI all use the same scope. Added a <code>blsc_scannable_post_types<\/code> filter to customise the list.<\/li>\n<\/ul>\n\n<h4>2.4.9<\/h4>\n\n<ul>\n<li>Scan history now shows every recorded entry (up to the 30 the plugin stores) instead of being capped.<\/li>\n<li>Domain and URL exclusions are now fully configurable from the Settings screen \u2014 enter the domains or URLs to skip during scans.<\/li>\n<\/ul>\n\n<h4>2.4.8<\/h4>\n\n<ul>\n<li>Replaced all direct cURL calls with the WordPress HTTP API (<code>wp_remote_*<\/code>) for link and image checking, including the batch scanner. Redirects are now followed via the HTTP API while still reporting redirect chains and loops.<\/li>\n<li>WP-CLI <code>blc export<\/code> now writes CSV to STDOUT only (use shell redirection, e.g. <code>wp blc export &gt; report.csv<\/code>); the arbitrary <code>--file<\/code> path option was removed.<\/li>\n<\/ul>\n\n<h4>2.4.7<\/h4>\n\n<ul>\n<li>WordPress.org coding-standards hardening: explicit output escaping in the admin dashboard and post meta box, documented safe <code>$wpdb<\/code> queries, and translators comments for all i18n placeholders (Plugin Check compliance).<\/li>\n<\/ul>\n\n<h4>2.4.6<\/h4>\n\n<ul>\n<li>Added a dismissible tip on the dashboard linking to the companion cloud scanner (brokenlinkscan.com) for a full-site crawl beyond posts and pages.<\/li>\n<\/ul>\n\n<h4>2.4.5<\/h4>\n\n<ul>\n<li>Replaced PHP short echo tags (<code>&lt;?=<\/code>) with full <code>&lt;?php echo<\/code> in the post meta box (WordPress.org coding-standards compliance).<\/li>\n<li>Bumped \"Tested up to\" to WordPress 7.0 and aligned the readme plugin name with the plugin header.<\/li>\n<\/ul>\n\n<h4>2.4.4<\/h4>\n\n<ul>\n<li>Hardened admin output escaping: replaced <code>phpcs:ignore<\/code> suppressions with explicit <code>wp_kses()<\/code>, <code>esc_attr()<\/code> and <code>(int)<\/code> casts in the dashboard view (WordPress.org coding-standards compliance).<\/li>\n<\/ul>\n\n<h4>2.4.3<\/h4>\n\n<ul>\n<li>Document outbound HTTP scanning behavior under the new \"External services\" section of the readme (WordPress.org guideline 6 compliance).<\/li>\n<\/ul>\n\n<h4>2.4.2<\/h4>\n\n<ul>\n<li>Removed the ACF field scanner from the free plugin (the code only ran when the Pro add-on enabled the related setting). ACF scanning is now provided entirely by the companion Pro plugin via the new <code>blsc_extra_links<\/code> filter.<\/li>\n<\/ul>\n\n<h4>2.4.1<\/h4>\n\n<ul>\n<li>Removed all licence checks and premium-feature gating from the free plugin code (premium features \u2014 auto-scan cron, email\/Slack notifications, ACF scanning, exclusions, CSV\/XLSX export, extended history \u2014 are now distributed via a separate companion plugin).<\/li>\n<li>Removed the now-unnecessary <code>load_plugin_textdomain()<\/code> call (handled by WordPress.org translation system since WP 4.6).<\/li>\n<li>Added extension hooks for add-on plugins (settings defaults\/sanitisation, scan-complete notification, scan\/exclusion filters, admin UI).<\/li>\n<\/ul>\n\n<h4>2.4.0<\/h4>\n\n<ul>\n<li>Added: Broken anchor detection (missing #id targets)<\/li>\n<li>Added: Redirect chain tracking and reporting<\/li>\n<li>Added: Slack notifications for scan results<\/li>\n<li>Added: Scan diff \u2014 compare results between consecutive scans<\/li>\n<li>Added: Health score dashboard with summary cards<\/li>\n<\/ul>\n\n<h4>2.2.0<\/h4>\n\n<ul>\n<li>Fixed: Plugin header version now matches internal constant<\/li>\n<li>Fixed: SSL_VERIFYHOST set to 2 (was incorrectly disabled, MITM vulnerability)<\/li>\n<li>Fixed: Plugin action links basename corrected (Settings link now works on Plugins page)<\/li>\n<li>Fixed: Double scan race condition \u2014 pending state now blocks duplicate starts<\/li>\n<li>Fixed: Raw $_POST passed to settings save without wp_unslash()<\/li>\n<li>Fixed: save_scan_history() query now uses $wpdb-&gt;prepare()<\/li>\n<li>Improved: Nonce caching in list table rows (performance)<\/li>\n<li>Improved: Hardcoded French strings in admin.js replaced with i18n keys<\/li>\n<li>Removed: Dead code (page_by_post method, by-post.php template, unused CSS, blsc_manual_ok_urls option)<\/li>\n<li>Added: Missing admin notice keys (settings_saved, export_empty)<\/li>\n<\/ul>\n\n<h4>2.1.0<\/h4>\n\n<ul>\n<li>Parallel HTTP checks via curl_multi<\/li>\n<li>Email alerts for new broken links<\/li>\n<li>Scan history and delta reporting<\/li>\n<li>XLSX export support<\/li>\n<li>Ignore list with persistent overrides<\/li>\n<li>By-post view integrated into dashboard<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<\/ul>","raw_excerpt":"Scan your posts, pages and custom post types for broken links and images \u2014 href, src, srcset included. No external service required.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/316836","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=316836"}],"author":[{"embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/pierreferrolliet"}],"wp:attachment":[{"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=316836"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=316836"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=316836"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=316836"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=316836"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/br.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=316836"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}