MDS072: external-link-check
Probe external http and https URLs; flag any returning a transport error or 4xx/5xx response.
This rule closes the gap with gomarklint’s external-link check
(issue #47). It is off by default and opt-in, like MDS068
(link-style). Network I/O has no place on the default mdsmith check
hot path. It reads the shared links: config block — the same block
MDS027 and MDS068 read. So external-skip, external-timeout, and
external-rate-limit sit beside site-root and style per kind.
The rule checks inline links ([text](url)), autolinks
(<https://example.com>), and images (). It probes over
the network only on native builds. The WebAssembly engine cannot reach
the network, so it treats every URL as not-probed and emits no MDS072
diagnostics. It never reports a URL as healthy on faith. A future host
bridge will let a WASM host such as the Obsidian plugin supply real
probe results.
# Settings
| Setting | Type | Default | Description |
|---|---|---|---|
links.external-skip | list | [] | Regex patterns; a matching URL is not probed |
links.external-timeout | string | "5s" | Per-request timeout as a Go duration |
links.external-rate-limit | int | 10 | Maximum concurrent in-flight requests; minimum 1 |
Each external URL is probed once per run with an HTTP HEAD request. A
URL whose HEAD returns 405 (Method Not Allowed) is retried with GET.
Redirects are followed; a final 2xx or 3xx passes. Results are cached
per URL for the run, so the same URL across many files costs one
request. A non-positive external-timeout falls back to 5s; a
rate limit below 1 clamps to 1.
# Config
Enable with defaults (5s timeout, 10 concurrent requests):
rules:
external-link-check: trueSkip intranet and example hosts, tighten the timeout, cap concurrency:
rules:
external-link-check:
links:
external-skip:
- "^https?://localhost"
- "^https?://127\\."
external-timeout: 10s
external-rate-limit: 5Disable:
rules:
external-link-check: false# Examples
# Good – no external URLs to probe
# No External Links
This file links to a [sibling document](good/no-external-links.md). It also
links to an [in-page anchor](#no-external-links). Neither is an external
URL. So the rule finds nothing to probe. It reports no diagnostics.The fixture suite omits a bad example on purpose. A fixture with a live
broken URL would hit the network on every go test run. The HTTP
behaviour lives in rule_test.go. That test drives a local
httptest.NewServer. It covers the 200, 404, 405-then-GET,
transport-error, and cache-hit paths.
# Diagnostics
| Condition | Message |
|---|---|
| URL returns 4xx or 5xx | external URL returned HTTP <code>: <url> |
| URL unreachable (transport error) | external URL unreachable: <url> (<error>) |
# See also
- MDS027 cross-file-reference-integrity
— validates local file and heading links; shares the
links:block. - MDS068 link-style
— enforces link path, extension, and form style; shares
links:.
# Meta-Information
- ID: MDS072
- Name:
external-link-check - Status: ready
- Default: disabled, opt-in. Network I/O keeps it off the hot path.
- Fixable: no
- Implementation: source
- Category: link
- gomarklint: external-link