=== Dragon Broken Links ===
Contributors: dragoncoreltd
Tags: broken links, link checker, seo, 404, links
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 1.1.3
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Find and fix broken internal, external and image links from your own server - no cloud, no credits, no monthly limits.

== Description ==

Dragon Broken Links scans your posts, pages and every other public post type (WooCommerce products included) for links, images, embeds and media that no longer work, and lets you fix them without leaving WordPress. The checking runs on your own server - there is no third-party checking service, no account, no per-link credits and no monthly quota.

= Why this one =

Most broken-link plugins either meter the free version to a few hundred links a month or push the real checking into a paid cloud subscription. Dragon Broken Links does the opposite: **internal and external checking is free and unlimited**, and it is built to be a good citizen on your server and on the sites it checks.

= What it checks =

* **Links, images, embeds and media** in every published post, page, product and custom post type - including inside reusable (synced) patterns - with no link limit. Narrow the list of post types under Settings if you want to.
* **Internal links without touching your site** - resolved against your database: links to deleted, trashed, unpublished or renamed content, and images missing from your uploads folder, are flagged instantly with no requests to your own server.
* **External links politely** - one request per unique URL no matter how many posts use it, a few at a time, never more than a handful to the same site in one go.

= No false alarms =

* A link is only marked **Broken** after a hard failure (404, 410, dead domain, connection refused) is confirmed on three separate checks.
* A first failure shows as **Suspected** and is re-checked before it counts.
* Ambiguous answers (bot-blocking 403s, rate limits, timeouts, server errors) go to **Needs review**, never counted as broken.
* Servers that refuse HEAD requests get a second, body-free GET before the answer counts.

= Fix in place =

* Edit a link's URL or remove the link (keeping its text) straight from the report - block-editor aware, so both the block data and the rendered HTML are updated together and the editor never reports invalid content.
* Dismiss a URL everywhere it appears, and restore it later from the Dismissed view.
* Re-check any single link on demand.
* **Scan now** with live progress, plus a background re-check every 15 minutes that works through every link on the cadence you choose - daily, weekly, monthly or never (a very large site, or slow hosts, can take longer).
* **WP-CLI**: `wp dragon-broken-links scan`, `list` and `recheck` for large or headless sites.

= Secure by design =

External checks refuse to reach private, internal, link-local, cloud-metadata and reserved addresses, re-validate every redirect hop, pin the connection to the address that was validated, and never read more than the first few kilobytes of a response.

= Works with Dragon Redirect Manager =

If [Dragon Redirect Manager](https://dragoncore.ltd/plugins/dragon-redirect-manager) is active, a broken internal link gets a one-click **Create redirect** action, so you can point the old address somewhere sensible without leaving the report.

= What the free version includes =

Everything above is free, fully functional and unlimited - no link cap, no cloud account, no upsell nags.

= Dragon Broken Links Pro =

[Dragon Broken Links Pro](https://dragoncore.ltd/plugins/dragon-broken-links-pro) adds, for a one-time price:

* Email, Slack and signed-webhook alerts on newly broken links.
* Bulk fixing and site-wide domain find-and-replace.
* Scheduled monitoring with history and CSV export.
* Scanning of links held in custom fields and comments.

= Multisite =

Works per site. Each site has its own report, settings and background re-check; there is no network-wide view.

== External services ==

This plugin makes no connection to Dragon Core or any third-party checking service, and sends no data about your site anywhere.

To test an external link it sends an HTTP HEAD request (then a GET, if HEAD is refused) to that URL from your own server, once per unique URL, with the User-Agent `Mozilla/5.0 (compatible; DragonBrokenLinks/<version>; +https://dragoncore.ltd/plugins/dragon-broken-links)`. The linked site therefore sees a normal request for that page coming from your server's IP address; nothing about your site, its content or its users is included. Internal links are checked against your database and are never requested. Requests to private, loopback, link-local and reserved addresses are refused.

== Installation ==

1. Install and activate the plugin from the Plugins screen, or upload the `dragon-broken-links` folder to `/wp-content/plugins/`.
2. Go to **Tools → Broken Links**.
3. Press **Scan now**. Links are re-checked automatically in the background afterwards.

== Frequently Asked Questions ==

= Does it use an external service? =

No. There is no cloud account, no API key, no per-link credits, and nothing is reported to Dragon Core or anyone else. The only outbound traffic is the request your server makes to each external link to see whether it still works (see "External services" above).

= Is there a limit on links or scans? =

No. Every unique URL is checked once per cycle, however many posts link to it, and there is no cap on links, posts or scans.

= What is the difference between Broken, Suspected and Needs review? =

**Broken** - failed on three separate checks with a hard error (404, 410, no such domain, connection refused). **Suspected** - failed once; it is re-checked before it counts as broken. **Needs review** - the site answered but ambiguously (403, 429, timeout, server error), which is often bot-blocking rather than a dead page; open it, and dismiss it if it works.

= Why is a working link shown as "Needs review"? =

Some servers answer automated requests with a 403, 429 or a timeout even though the page is fine for real visitors. Rather than cry wolf, Dragon Broken Links flags those for you to decide, and never counts them as broken automatically.

= Will checking links slow down or overload my site? =

No. External links are checked in small, throttled batches within a fixed time budget, and each unique URL is checked only once. Internal links are resolved from the database rather than by making requests to your own site.

= Does it check images? =

Yes - `<img>` sources, plus iframes, video, audio and embeds in your posts and pages. Internal images are checked against the uploads folder (including resized variants); external ones are requested like any other link. Gallery thumbnails and `srcset` variants of an image that exists are not checked separately.

= Can I schedule scans? =

Links are re-checked automatically in the background (every 15 minutes, working through every link daily, weekly or monthly - your choice under Settings), and a post is re-scanned whenever you save it. Scheduled full scans with history and CSV export are part of Pro.

= Does it scan WooCommerce products and custom post types? =

Yes. Every public post type is scanned by default - posts, pages, WooCommerce products and any custom post type your theme or plugins register. Untick the ones you do not want under **Settings → Content scanned**.

= Does it scan comments or custom fields? =

Not on its own: links in comments and custom fields are covered by Dragon Broken Links Pro.

= I'm coming from Broken Link Checker - can I keep my dismissed links? =

Yes. If Broken Link Checker's data is still in your database, **Settings → Import from Broken Link Checker** carries your dismissed links over so you do not have to re-triage them. Nothing is removed from Broken Link Checker.

= My scan does not seem to run on its own. =

Background scanning uses WP-Cron, which only runs when your site gets traffic. On a quiet site, click **Scan now**, or run `wp dragon-broken-links scan` from WP-CLI.

= Does editing a link change my content safely? =

Yes. Fixes are applied through the block parser, updating both the block data and the rendered HTML together, so the editor never reports invalid content. Classic (non-block) content is handled too, and every fix creates a revision you can restore from.

= Does it work on multisite? =

Yes, per site: each site keeps its own report, settings and background re-check.

== Screenshots ==

1. The report: every broken link, where it was found and a plain-English reason - with Re-check, Edit URL, Remove link, Dismiss and Create redirect on each row.
2. Scan now runs on your own server with live progress ("Checking links - 18 remaining") - no cloud, no credits.
3. Ambiguous answers (bot-blocking 403s, rate limits, server errors) go to Needs review and are never counted as broken.
4. Dismissed links stay out of the report until you restore them.
5. Settings: which post types are scanned, how often links are re-checked, the request timeout and the data-retention opt-in; a Broken Link Checker import appears here when that plugin's data is present.

== Upgrade Notice ==

= 1.1.3 =
Every public post type (products and custom post types included) is now scanned, and the background re-check cadence is yours to choose. Click Scan now once after updating to read the newly covered content.

= 1.1.2 =
Copy polish only. If you are on 1.0.x, see the 1.1.0 notes - that update fixes a bug that could corrupt other blocks in a post when a link was edited or removed.

= 1.1.1 =
The report's status key is now visual. If you are on 1.0.x, see the 1.1.0 notes - that update fixes a bug that could corrupt other blocks in a post when a link was edited or removed.

= 1.1.0 =
Important: fixes a bug where editing or removing a link could corrupt other blocks in the same post. Also flags deleted pages and missing images properly, keeps your dismissals across edits, and checks links far faster in the background. Update straight away.

== Changelog ==

= 1.1.3 =
* New: every public post type is scanned - WooCommerce products and custom post types included - with a checklist under Settings to narrow it down.
* New: the background re-check cadence is a setting: daily, weekly, monthly or never.
* Changed: sites updating from 1.1.2 re-read all content on the next scan so the newly covered post types are picked up.

= 1.1.2 =
* Copy: plainer punctuation throughout the report, settings and documentation.

= 1.1.1 =
* Report: the status key is now three labelled pills instead of a paragraph, so Broken / Suspected / Needs review are recognisable at a glance.
* Report: dismissed and not-yet-checked rows get a neutral pill.

= 1.1.0 =
* Fixed: editing or removing a link could corrupt the other blocks in that post (JSON escapes in block attributes such as `&`, `--` or backslashes lost their backslash on save). Every fix is now saved exactly as the editor would.
* Fixed: dismissed links (including those imported from Broken Link Checker) were forgotten when the post containing them was next edited.
* Fixed: a link to a domain that no longer exists was reported as "needs review" instead of broken.
* Fixed: "Remove link" did nothing for URLs containing a query string.
* Fixed: "Create redirect" on a query-style link (/?p=123) would have redirected the homepage; such links now explain why they cannot be redirected.
* Fixed: switching the weekly re-check back on after turning it off left every already-checked link never re-checked.
* New: internal links to deleted, trashed, draft or renamed pages are now detected (old slugs are followed), and internal images missing from the uploads folder are flagged - all from the database, with no requests to your own site.
* New: iframes, video, audio, embeds and reusable (synced) patterns are scanned.
* New: Dismissed view with a Restore action; the report shows counts per view, a plain-English reason for every link, a status legend and a proper first-run message.
* Improved: background re-checks now run every 15 minutes within a fixed time budget, so sites with many thousands of links get through them in about a week; "Scan now" always re-reads every post, never runs two scans at once, and shows which phase it is in.
* Improved: servers that refuse HEAD get a body-free GET before the answer counts, reducing false "needs review" results.
* Security: the connection is pinned to the address that passed the private-network check, closing a DNS-rebinding window; multicast, NAT64, 6to4, Teredo, documentation and benchmark ranges are refused; a redirect back onto your own site is never followed; no more than 64 KB of any response is read.
* Housekeeping: the manual scan stays inside PHP's time limit; the uninstall opt-in now removes every trace; the cron event self-heals on sites that never ran the activation hook.

= 1.0.8 =
* Critical fix: the admin area could show a fatal error on sites running 1.0.4–1.0.7 because the Broken Link Checker import helper was not being loaded. Every admin screen now loads correctly. Please update straight away.

= 1.0.7 =
* Reliability: the background re-check now stops within a fixed time budget so a large backlog can never overrun a single cron run.

= 1.0.6 =
* Compatibility: tested up to WordPress 7.1.
* Housekeeping: corrected the contributor name in the plugin readme.

= 1.0.5 =
* Documentation: clarified that link checking requests external URLs from your server while using no third-party service.

= 1.0.4 =
* Data safety: uninstalling the plugin no longer deletes its data unless you explicitly opt in first (new setting under Settings) - a reinstall now picks up exactly where you left off.
* New: one-click import of your dismissed links from Broken Link Checker, so a site switching over does not have to re-triage.

= 1.0.3 =
* Polish: one-row toolbar (scan, status, filters and search together), always-visible row actions, segmented filter control, more breathing room under the admin bar, and severity markers that now survive WordPress core's last-row style. Grammar fix in the scan summary.

= 1.0.2 =
* New look: the report and settings now wear the Dragon design system - clearer status pills with reason codes, severity markers on rows, monospace URLs, and a consistent Dragon Core header. No behaviour changes.

= 1.0.1 =
* Added a filter so Dragon Broken Links Pro can scan additional content sources such as custom fields and comments.

= 1.0.0 =
* Initial release.
