Version 1.4.3

May 21, 2026

Fixed

  • Fixed Bluesky reply threading regression introduced in 1.3.1. Bridgy stores Bluesky webmentions' hEntryUrl in either DID form (/profile/did:plc:.../post/{rkey}) or handle form (/profile/{handle}/post/{rkey}), but the at://bsky.app conversion in extractInReplyToUrls() always emits the DID form. Parent-lookup did an exact-string match on hEntryUrl, so replies whose parent was stored in handle form fell back to top-level. Resolution now falls back to matching by the post's rkey (a globally-unique TID), mirroring the existing Mastodon /web/statuses/{id}/@user/{id} fallback. Both directions (resolveParentWebmention() and resolveChildWebmentions()) handle the variation.

Added

  • Added a backfill migration (m260521_000000_backfill_parent_ids_bluesky) that re-resolves parentId for existing webmentions with the new rkey matching. The migration only touches rows that still have parentId = NULL, so prior backfills are preserved. As a side effect, it also picks up any Mastodon stragglers received after the 1.1.0 backfill ran.
  • Added a webmention/backfill/bluesky console command with a --dry-run flag so the backfill can be previewed before applying. Useful for verifying matches against production data without writing anything.

Version 1.4.2

May 13, 2026

Fixed

  • Broadened the enshrined/svg-sanitize Composer constraint even more to >=0.19,<1.0 to track Craft's own version bumps (Craft 5.6 pinned ~0.19.0, Craft 5.9 pinned ~0.22.0). The plugin's usage of the Sanitizer API is stable across all 0.x releases, so the wider range introduces no functional changes — only avoids future "satisfiable but conflicts with another require" install errors when Craft moves its constraint forward.

Version 1.4.1

May 13, 2026

Fixed

  • Widened the enshrined/svg-sanitize Composer constraint to ^0.19 || ^0.20 || ^0.21 to avoid a dependency conflict on sites running Craft 5.6+ that already pull in a newer svg-sanitize version. The plugin's usage (Sanitizer construction + sanitize() call) is stable across all supported versions.

Version 1.4.0

May 13, 2026

Security

  • Critical: Added safeOutboundRequest() helper enforcing per-hop SSRF protection on all outbound Guzzle requests. Source fetches and avatar downloads now validate resolved IP addresses against private/reserved ranges.
  • Critical: Response-body size caps of 5 MB for source HTML and avatars enforced via streaming on_headers guard and byte-counting sink, preventing memory exhaustion from oversized responses.
  • High: SVG avatars are now sanitized via enshrined/svg-sanitize before being written to the asset volume, removing <script>, event handlers, and <foreignObject>.
  • High: Avatar file extension is now derived from Content-Type header first, with URL path as fallback, and validated against an allowlist (jpg, jpeg, png, gif, webp, svg). Files with unrecognised MIME types or disallowed extensions are refused.
  • High: Replaced catastrophic-backtracking regexes in Sender::_findEndpointInBody() and Sender::_findEndpointInHeaders() with DOMDocument/XPath and structured Link-header parsing, eliminating ReDoS risk on attacker-controlled page bodies and headers. Body-parsed rel values are now compared case-insensitively, matching the existing header behavior and the HTML living standard.
  • Medium: Hardened the URL-extraction regex in extractTargets() against ReDoS. The % character was removed from the path bare character class so percent-encoded sequences (%XY) have exactly one valid parsing instead of two, eliminating the exponential-backtracking root cause. The regex's existing recognition of markdown-link URLs with embedded parens (Wikipedia's _(physicist) style), ftp:// URLs, trailing slashes, and percent-encoded paths is preserved unchanged.
  • Medium: Added a unique database index on (source, target, targetId, targetSiteId) in the webmentions table to prevent duplicate rows from concurrent queue workers. A migration deduplicates existing rows (keeping the most recently processed record, with preference for rows containing thread reply links). An IntegrityException guard in the queue job handles any remaining races gracefully.
  • Low: Server filesystem paths are now redacted from errorTrace before storage in the database. Stored traces use [plugin] and [craft] placeholders instead of absolute paths visible to anyone with DB access.

Added

  • The trustedSourceHosts setting now also exempts matching sources from the private/reserved-IP check (in addition to the rate limit). This lets self-hosted senders on internal networks, e.g. homelab Mastodon, intranet Micropub servers, deliver Webmentions to public Craft sites without disabling the SSRF guard for everyone. Trust remains opt-in and admin-controlled; unlisted sources are still validated as before.

Version 1.3.1

May 8, 2026

Fixed

  • Fixed "Unable to parse URI: at://" failures when processing Bluesky webmentions routed through Bridgy. Bridgy's Bluesky comment pages contain at:// ATProto URIs in their <a href> elements, which crash Guzzle's URL parser. The link iteration in validateWebmention() now pre-filters non-http(s) hrefs and is wrapped in a try/catch, so a single unparseable link no longer kills the whole validation loop. Affected failure rows clear on retry.
  • Fixed Bluesky reply threading. mf2 in-reply-to values pointing at at://did:.../app.bsky.feed.post/{rkey} are now converted to their canonical https://bsky.app/profile/{did}/post/{rkey} form so they can be matched against existing webmentions' hEntryUrl. Replies to other Bluesky posts now nest correctly under their parent in threaded display.

Version 1.3.0

May 6, 2026

Security

  • Fixed a stored XSS vulnerability in author and entry URLs displayed in the control panel and front-end Webmention templates. The plugin now validates URL schemes (only http(s): allowed) at storage and at render time, so javascript:, data:, and similar URI schemes from a malicious source page can no longer be rendered as clickable links. URLs containing whitespace, userinfo (user:pass@…), illegal host characters, or longer than 2048 characters are also rejected. Sites running prior versions should upgrade.
  • Added a per-IP rate limit on the public Webmention endpoint (default: 100 submissions/hour, configurable via the new rateLimitPerHour setting). High-traffic non-Bridgy senders that previously had no cap will now receive 429 responses past the limit; raise the limit or add the host to trustedSourceHosts if needed. Set to 0 to disable.
  • Added a failure-backoff threshold (default: 5, configurable via the new failureBackoffThreshold setting). Once a (source, target) pair has been recorded as a failure this many times, further submissions for that pair are no longer queued or fetched until the cleanup command purges the record. The failure row's attempt counter still increments so repeated abuse stays visible in the Failed Webmentions view.
  • Submissions for the same (source, target) pair received within a 5-minute window are now deduplicated at the controller, so a flood of identical submissions cannot amplify outbound HTTP fetches.

Added

  • Added a trustedSourceHosts setting (default: ['brid.gy']) that lets configured source hosts bypass the per-IP rate limit, so viral traffic routed through Bridgy isn't dropped during a spike. Subdomain matches are included automatically, so the default covers fed.brid.gy and bsky.brid.gy.

Fixed

  • Stopped HTML-encoding URL field values during parsing. URL escaping is now handled at the rendering layer, so stored URLs no longer have & characters corrupted to &amp;.

Version 1.2.0

March 22, 2026

Added

  • Added a Failed Webmentions view to the control panel. Webmentions that cannot be processed are now recorded in a new webmention_failures table instead of disappearing silently into the logs.
  • Failed webmentions are shown in a dedicated "Failed" subnav item with a badge displaying the current failure count.
  • The failures table shows source, target, error message, attempt count, and last attempted timestamp for each failure.
  • Added per-row Retry and Dismiss actions. Retry re-queues the webmention for processing and removes the failure record; a successful retry also clears the record automatically.
  • Added bulk Retry All and Dismiss All actions above the failures table.
  • If a source+target combination fails repeatedly, the existing failure record is updated (incrementing the attempt count) rather than creating duplicates.
  • Added a failureRetentionDays setting (default: 30) to automatically prune old failure records.
  • Added a webmention/cleanup/failures console command to manually prune failure records older than the retention setting.
  • Added Bluesky author fallback via the public AT Protocol API. When a webmention from Bridgy Fed (bsky.brid.gy) contains no author data in its mf2, the plugin now extracts the Bluesky DID from the URL and fetches the author's display name, profile URL, and avatar from public.api.bsky.app. The avatar is stored locally via the existing asset saving flow.
  • Tracking parameters (utm_*, fbclid, gclid, and similar) are now stripped from URLs during normalization to prevent duplicate webmention records for the same source.

Version 1.1.3

March 22, 2026
  • Fixed avatar saving failures in queue context caused by the volume filesystem using a relative Base Path that didn't resolve correctly in CLI.
  • Fixed file extension detection for avatar URLs containing query strings (e.g. avatar.webp?cb=123).
  • Added validation for empty or truncated avatar responses from CDNs.
  • Added error handling for cleanImage() failures that could destroy the temp file before asset saving.
  • Fixed a crash when processing Bluesky webmentions with at:// protocol URIs in in-reply-to data.

Version 1.1.2

March 21, 2026
  • Fixed a race condition where concurrent queue jobs processing webmentions from the same author would write to the same temporary file path, causing avatar assets to fail with an "Unable to copy stream" error.
  • Avatar assets are now reused across webmentions from the same author instead of being re-downloaded and re-saved for each incoming webmention.

Version 1.1.1

March 14, 2026
  • Missed to bump up the $schemaVersion…

Version 1.1.0

March 14, 2026
  • Added threaded/nested display for webmentions. Replies to other webmentions are now tracked via in-reply-to from mf2 data and displayed as nested threads.
  • Added parentId to the Webmention element, linking replies to the webmention they respond to.
  • Added reverse resolution: when a new webmention arrives, existing orphan replies are automatically linked to it.
  • Added retroactive backfill: existing webmentions with in-reply-to data are connected to their parents on migration.
  • Added "Threaded Display" setting in the control panel to toggle between threaded and flat display.
  • Added getThreadedWebmentions(), getThreadedWebmentionsForElement(), getParentWebmention(), getChildWebmentions(), and getInReplyToUrl() template helpers.
  • Added parentId() and hasParent() query params to WebmentionQuery.
  • Improved accessibility of the default template with semantic list markup and ARIA labels.
  • Added retry logic for avatar asset saving to handle transient stream errors.

Version 1.0.7

March 7, 2026
  • Fixed a database error caused by oversized name values from senders that populate the h-entry name property with UI chrome and article excerpts due to the mf2 implied name algorithm.

Version 1.0.6

March 5, 2026
  • Fixed Bridgy source URL type and site detection to match path segments only, preventing false positives where e.g. app.bsky.feed.post in Bluesky URLs would incorrectly match as a mention type.

Version 1.0.5

March 4, 2026
  • Added alt text support for author photos. (#9)
  • Added loading="lazy" to the webmention Twig template.
  • Improved reliability: unresolvable URLs (local/test TLDs, localhost, IP addresses) are now detected via DNS lookup and handled gracefully without blocking the queue.
  • Improved performance: avatar images are no longer loaded twice, and Guzzle connection/request timeouts are now configured to better handle slow responses.
  • Improved URL matching by normalizing URLs for comparison.
  • Fixed multiple bugs in webmention parsing and avatar handling, including null checks for representative h-card property access, graceful handling of missing published dates, avatar filename collision prevention (by hashing the full URL), and correct Bridgy type detection.
  • Fixed form submission check to use URL parameter.

Version 1.0.4

May 16, 2025
  • Added support for eager-loading webmentions
  • Added new element.getTotalWebmentions() and getTotalWebmentionsByType() methods, which support eager-loading as well.

Version 1.0.3

May 12, 2025
  • Improved performance by automatically eager-loading avatars the first time getAvatar() is called. (#13)
  • Added the “Host” condition rule type. (#12)
  • “Source” and “Target” condition rules no longer have “has a value” or “is empty” operators. (#12)
  • authorName values now use the h-card’s nickname property as a fallback. (#10)
  • Fixed a bug where webmention validation wasn’t catching ConnectException errors.
  • Fixed a bug where jobs for webmentions whithout a valid backlink to the target got stuck in the queue.
  • Fixed a bug where Bluesky source URLs from Brid.gy weren’t always being stored in their entirety.

Version 1.0.2

March 21, 2025
  • Fixed a bug where getting the the avatar photo from the parsed representative h-card would fail because the URL was the value inside of an array instead of being a string. Now the plugin supports both cases.

Version 1.0.1

March 15, 2025
  • Added avatarId, host, and properties as optional table attributes
  • Fixed Bluesky (via Bridgy) avatars: if an avatar image has no extension, the extension is now determined by the respective MIME type

Version 1.0.0

March 9, 2025
  • Added Craft 5 compatibility.
  • Added the “Avatar Location” setting.
  • Avatar assets are now accessible via webmention.avatar.
  • Webmentions now store which element they are associated with.
  • Added the ability to update webmentions from the control panel.
  • Added the resave/webmentions CLI command.
  • Added the webmention/example-template CLI command.
  • Added the webmention/update CLI command.
  • Added the webmention/update-avatars CLI command.
  • Added the getWebmentions() and getWebmentionsByType() element behaviors
  • Added support for the Bridgy site types mastodon, bluesky, github, and reddit.
  • Added a new icon based on Paul Robert Lloyd’s IndieWeb icon designs
  • Fixed the regex that scans for URLs in entries so that it now correctly handles Markdown links
  • Lots of smaller bugfixes and improvements

Version 0.3.1

April 2, 2017
  • Changed the retrieval method for links within an entry to fix a bug where a very long article with many links would lead to a PHP execution timeout
  • Minor bugfixes and improvements

Version 0.3.0

January 6, 2017
  • Webmention sending functionality implemented
  • Setting added: Entry Types (for Webmention sending)
  • New “Webmention Switch” field type

Version 0.2.0

June 7, 2016
  • Webmentions are now stored as Craft elements (ElementType: Webmention_webmention)
  • Improved backend functionality: Webmentions are displayed under the tab Webmentions and can be deleted
  • The plugin now sets the type property of an incoming Webmention correctly, based on the Microformats properties u-like-of, u-like, u-repost-of, and u-repost.

Version 0.1.0

June 3, 2016
  • First version