Who gets what
- Logged out, browser supports speculation rules: native eager prerender.
- Logged in, browser supports speculation rules: native prefetch on hover or pointer-down (moderate eagerness). Prefetch fetches the document without running its scripts, so analytics, Heartbeat and similar page JavaScript do not fire.
- Browser without speculation rules, or a page with none (for example a site without pretty permalinks): the fallback script.
Fallback script
- The script runs only if
HTMLScriptElement.supports('speculationrules')is false or missing (Firefox before 94, Safari before 16, older Chromium), or the page contains noscript[type="speculationrules"]. Otherwise it does nothing and the native rules do the work. - Hovered and touched links go into a priority queue, and links in the viewport (rescanned 100 ms after scrolling stops, and when a
dialogopens) into a normal queue. Every 2 seconds up tomaxRPSURLs (default 3) are prefetched, priority first, and the timer stops once both queues are empty. - A URL is prefetched with
<link rel="prefetch" as="document">where the browser supports it, otherwise with a credentialedfetch(). URLs already prefetched, includingrel="prefetch"links other plugins put in the page, are not fetched again, and the current page is never prefetched. - It skips URLs with a query string, any URL containing
nonce, and URLs matching the ignore lists. maxRPScan be overridden by definingwindow.FPConfigbefore the script runs.
Gotchas
- GET requests with side effects. Core keeps speculation off for logged-in users largely because some page views change state. Action links carry nonces and query strings, so they are already excluded. Plain views that mark things read are not, which is why a member’s own BuddyPress profile area (messages, notifications, mentions, friend requests, group invites) is excluded. Any other plugin with a view that changes state should add its URLs to
lh_jumpstart_return_ignore_prefix_urls, or skip the side effect when the request carriesSec-Purpose: prefetch. - Before 1.1.0 the ignore lists were passed to core as an
href_excludekey on the configuration, which core discards, so they only ever reached the fallback script. - The script is registered through the bundled
LH_Register_file_class(includes/lh-register-file-class.php), whose retirement across the lh- plugins is tracked in lh-tasks #148564.