All plugins

Dragon Broken Links

Broken link checker that runs on your own server: find and fix dead internal, external and image links. No cloud, no credits, no monthly limits.

Find and fix broken links with a local engine - unlimited checking, no cloud account, no credits.

Requirements

WordPress 6.2+, PHP 8.0+. No external services: all checking runs on your own server. Email alerts, when switched on, go out through your site's own mail setup (wp_mail()). Every screen, email and notice is translatable; community translations come from translate.wordpress.org.

Getting started

  1. Activate the plugin and open Tools → Broken Links.
  2. Press Scan now. The scan runs in bounded batches: it reads every link, image, iframe, video/audio source and reusable (synced) pattern in your published content (patterns are followed wherever they sit, including inside Group and Columns blocks) (every public post type, or the ones you tick under Settings), dedupes the URLs (each unique URL is checked once, however many posts share it), then checks them. The status line shows which phase it is in ("Reading posts 12 of 40", then "Checking links - 57 remaining").
  3. Every press of Scan now re-reads all posts; a post is also re-scanned whenever it is saved, and every post that shows a synced pattern is queued for a re-scan after that pattern is edited, unpublished or deleted. Each background run re-scans up to 250 of those posts (fewer when the run spends its time budget checking links), and while posts are queued a follow-up run comes back every couple of minutes, so a pattern used in a few hundred posts is picked up within minutes to an hour; pressing Scan now works through the queue faster. Background re-checks run every 15 minutes on WP-Cron, inside a 20-second budget, working through every link on the cadence chosen under Settings - daily, weekly (the default), monthly or never (a very large site, or slow hosts, can take longer). While links are waiting to be checked, a follow-up run comes back every couple of minutes until the queue is clear, so a first scan of thousands of links finishes in hours. If WP-Cron is turned off (DISABLE_WP_CRON), the report says so: background checks then run only when your server calls wp-cron.php, and Scan now works in the meantime.

How checking works

  • Internal links are never fetched over HTTP. They resolve against your own database: links to trashed, draft, deleted or renamed content (old slugs are followed) and images missing from the uploads folder are detected instantly, and a slow site can't false-flag itself. Archive, term and custom-route URLs, WordPress's own paths and real files in the site root are left as OK rather than guessed at.
  • External links are fetched politely (a few at a time, at most three per site per batch) with a HEAD request and a body-free GET fallback for servers that refuse HEAD; no more than 64 KB of any response is ever read. Redirects are followed hop-by-hop with safety checks on every hop, and a redirect back onto your own site is never followed.
  • Three buckets, not one: clear failures (404, 410, dead domain, connection refused) become Suspected and are only promoted to Broken after three separate failures - a flaky server never instantly pollutes your report. Ambiguous responses (403, 429, timeouts, server errors) go to Needs review and are never auto-promoted, because sites that block bots aren't broken. Every row shows a plain-English reason.
  • Excluded URLs: a URL excluded by a rule (the dragonbrokenlinks_should_check_url filter) is not fetched. It is reviewed again on the re-check cadence and at the start of every Scan now, so lifting the rule brings it back into the report.
  • Internal links are checked in bulk: the three-per-site politeness limit applies only to external requests, so internal links and images are not held back by it. Each batch checks up to 40 links, internal and external together, and when one website has many links waiting, links to other websites are checked alongside them instead of queueing behind them.
  • Not checked: mailto, phone, SMS, fax and other non-web links, and same-page #anchors.
  • Security: requests to private, internal, link-local, cloud-metadata, multicast and IPv6-transition addresses are refused, and the connection is pinned to the address that passed that check.

Fixing from the report

Each row offers:

  • Edit URL - change the URL in that post (the prompt is pre-filled with the current one). The fix is block-aware: block data and rendered HTML are updated together, every JSON escape is preserved, and a revision is created so you can undo (on post types that keep revisions; WooCommerce products do not). A page built with Elementor, Beaver Builder or Bricks renders from the builder's own copy of the content, so the report asks you to fix its links in the builder instead. A link that comes from a synced pattern is not in the post itself: the report says which pattern holds it and offers to open that pattern in the editor, where one change fixes it everywhere the pattern is used.
  • Remove link - unlink the anchor but keep its text (or keep the image, for image links). On a button block the button and its text stay; only the link goes.
  • Re-check - check that URL again right now.
  • Dismiss everywhere - hide a URL you've decided is fine, in every post it appears in. Dismissed links stay dismissed across scans and edits; the Dismissed view lists them with a Restore action. The view's count only includes dismissed links that still appear in a post (a dismissal imported from Broken Link Checker for a link you no longer use is kept but not counted).
  • Create redirect - if Dragon Redirect Manager is active, turn a broken internal URL into a redirect in one click (path-style links only - a /?p=123 link has no path of its own).

Custom fields and comments

Under Settings → Also scan you can tick Custom fields and Approved comments (both off by default, because each adds a database query for every post read).

  • Custom fields: every http:// or https:// address in a post's public custom fields (field names that do not start with an underscore) is checked. Punctuation after an address in running text (a full stop, comma, closing bracket) is not treated as part of it.
  • Approved comments: the links in a post's approved comments (up to 200 per post) are checked. A post is read again at the end of the request in which one of its comments is approved, unapproved, edited, deleted or posted already approved.
  • Links found here appear in the report against the post they belong to. Edit URL, Remove link and domain replace only change post content, so the report tells you to fix these links where they are stored.
  • Ticking or unticking either box makes the background re-check read every post again, so the change shows up without saving each post.

Scheduled full re-scan

Settings → Full re-scan (weekly by default; daily or off) reads every post again in the background on that schedule, so links that reached a post without it being saved - imports, custom fields, direct database changes - are found. It does not run a separate job: it rewinds the post reader, and the background re-check works through it in its usual 20-second runs. The first background run after you update only starts the clock. If a scan is still reading posts when a re-scan is due, it finishes first and the re-scan starts on a later run. Link re-checks keep their own cadence (Re-check links).

Email alerts

Tick Settings → Email alerts to get one email listing the links that have newly become broken.

  • Who gets it: the addresses in Send to (separate several with commas, up to 20), or the site admin email when the box is empty.
  • When: when a scan finishes. If a background scan keeps going (a large site), links that have waited an hour are sent by the next background run. A link that breaks when you Re-check or Restore it by hand is sent straight away (or with the running scan's email, if a scan is in progress).
  • What: each link with its HTTP status or reason, and a link to the report. A link already broken is not sent again; no email is sent when nothing new broke.
  • If your mail setup refuses the email, or there is no valid address to send it to, the links are kept (up to 500) and sent with the next one.

Bulk tools

Tools → Broken Links → Bulk tools has three tools:

  • Replace a domain - enter the old and new domain (or paste a URL; only its domain is used). Every broken, suspected and needs-review link on the old domain, with or without www. or a port, is rewritten to the new domain exactly as you typed it; the path, query and fragment stay as written. Working links are not touched. Each post is changed through the same block-aware fixer as Edit URL, so posts and pages get a revision. Links in custom fields, comments and synced patterns, links in posts you cannot edit, and links written in a form whose domain cannot be changed safely are counted as skipped. Pages built with Elementor, Beaver Builder or Bricks are skipped and named in the result, so you can fix them in the builder.
  • Dismiss all needs-review links - dismisses every link in the Needs review view. A link used in a post you cannot edit is left alone. Restore any of them from the Dismissed view.
  • Export findings (CSV) - downloads the broken and needs-review links, one row for each place a link is used: URL, state, status, type, the post it is in, the post ID and when it was last checked (UTC). Cells a spreadsheet would run as a formula are prefixed with a quote.

If the site previously ran Broken Link Checker, Tools → Broken Links → Settings carries your dismissed links across so you don't re-triage them. Nothing is removed from the other plugin.

WP-CLI

bash
wp dragon-broken-links scan                 # run a full scan to completion (waits if another scan is running)
wp dragon-broken-links scan --fresh         # re-read every post even mid-scan
wp dragon-broken-links list [--status=...]  # list links: broken, warning, suspected or dismissed
wp dragon-broken-links recheck <url_id>     # check one URL now and print its new state

Data & privacy

The plugin stores its link index in two tables of your own database. Nothing leaves your server except the outbound link checks themselves and, if you switch them on, the alert emails. Uninstalling keeps your data by default so a reinstall picks up where you left off; tick Delete all data on uninstall in Settings if you want removal instead.

Troubleshooting

  • Nothing gets checked between visits - WP-Cron only fires on traffic. Use "Scan now", the WP-CLI command, or a server cron.
  • A working site shows as Needs review - its firewall is blocking automated checks (common with 403/429). That's what the bucket is for: eyeball it and dismiss.

Uninstall

Deleting the plugin keeps all its data by default, so a reinstall picks up where you left off. To remove everything on uninstall, tick Delete all data on uninstall in the plugin's settings first (this sets the dragonbrokenlinks_delete_data_on_uninstall option).