3.19: Preload fonts external domains
Enhancement opportunity The current implementation restricts processing of external CSS and font files to a predefined allowlist of domains.
This approach limits flexibility and requires frequent maintenance to support new valid sources.
Transitioning to an exclusion-based model, where all external domains are processed by default unless explicitly excluded due to known conflicts, would improve compatibility, reduce manual configuration, and future-proof the system against third-party domain changes.
User story
As product, I want the logic to process all external CSS and font files by default, so that new or previously unsupported domains don’t require manual allowing and integration becomes hands-free for all users.
Acceptance Criteria
- The system processes external CSS and font resources from any domain by default
- A centralized exclusion list is used to block processing from specific domains known to cause issues
- Exclusions are controlled by a filter (no UI)
- Exclusions are managed through a backend exclusion box and included in the Dynamic Lists content
- The old mechanism to allow specific domains is deprecated in favor of the new exclusion-based model
N/A
Additional context Initial discussion here - https://group-onecom.slack.com/archives/C08F4LZB2QG/p1748344684733299
QA Note: Test page template with fonts loaded from multiple external domains. Template: https://github.com/wp-media/rocket-test-data/blob/master/templates/preloadfonts_multiple_external_fonts.php Page URL: https://rocketlabsqa.ovh/preloadfonts_multiple_external_fonts/
Hello hello,
This issue concerns two repos:
Scope a solution
To implement this feature, we must transition from a hardcoded allowlist approach to an exclusion-based model for external font processing. This involves modifying the JavaScript beacon to process all external domains by default, implementing backend exclusion management through dynamic lists, and providing developer filters for custom exclusions.
Rocket-Scripts
We need to replace the logic behind this with something like:
// Process ALL external CSS links, filter by exclusions instead of allowlist
const links = [
...document.querySelectorAll('link[rel="stylesheet"]')
].filter(link => {
try {
const linkUrl = new URL(link.href);
const currentUrl = new URL(window.location.href);
// Only process external domains (different origin)
if (linkUrl.origin === currentUrl.origin) {
return false;
}
// Check exclusions instead of allowlist
const exclusions = this.config.external_font_exclusions || [];
return !exclusions.some(exclusion => link.href.includes(exclusion));
} catch (e) {
return false;
}
});
WP Rocket
- Backend configuration In the Subscriber:
- Add this method:
<?php
/**
* Get external font exclusions for beacon configuration
*
* @since 3.19.1
* @return array
*/
private function get_external_font_exclusions( array $exclusion ): array {
// Merge with dynamic lists exclusions
$lists = $this->data_manager->get_lists()->external_font_exclusions ?? [];
return array_merge( $exclusions, (array) $lists );
}
Also add this: 'rocket_external_font_exclusions' => 'get_external_font_exclusions', to get_subscribed_events.
- In
add_custom_data()here Add
/**
* Filters the external font domains to exclude from processing
*
* @since 3.19.1
*
* @param array $exclusions Array of domains to exclude from external font processing
*/
$data['external_font_exclusions'] = wpm_apply_filters_typed( 'string[]', 'rocket_external_font_exclusions', [] );
- Dynamic Lists Support (DynamicLists.php):
- Create this method:
<?php
/**
* Get the external font exclusions
*
* @since 3.19.1
* @return array
*/
public function get_external_font_exclusions(): array {
$lists = $this->providers['defaultlists']->data_manager->get_lists();
return $lists->external_font_exclusions ?? [];
}
Effort estimation:
M
This touches multiple systems (JavaScript beacon, PHP backend, dynamic lists, filters) and requires careful testing to ensure no regressions with existing font providers. The implementation is straightforward but the coordination across multiple components and thorough testing bumps it to medium effort.
Beacon Part
I'm worried about this new development because I think it could be resource intensive on the beacon part, we could be fetching a lot of css contents that may not contain any font related rule. If we are ready to overlook this, then that's fine, otherwise, maybe we should think deeper and find another way to process these external fonts, if we are to continue with this. @wordpressfan WDYT?
PHP Part
private function get_external_font_exclusions(): array { /** * Filters the external font domains to exclude from processing * * @since 3.19.1 * * @param array $exclusions Array of domains to exclude from external font processing */ $exclusions = wpm_apply_filters_typed( 'string[]', 'rocket_external_font_exclusions', [] ); // Merge with dynamic lists exclusions $lists = $this->data_manager->get_lists()->external_font_exclusions ?? []; return array_merge( $exclusions, (array) $lists ); }
We already have the data_manager as a dependency in the subscriber here. I'd say to build on top of it instead of adding a new one in the controller. going forward, can we add this wpm_apply_filters_typed( 'string[]', 'rocket_external_font_exclusions', [] ); directly to add_custom_data method and then make this get_external_font_exclusions method public and allow it accept an array as argument and move it to the subscriber class while hooking it to rocket_external_font_exclusions. The logic will be preserved as it is except the new array argument that's being passed.
@Miraeld WDYT?
Yea I Agree, we could move the logic from the controller into the Subscriber where we already have data_manager
Grooming is updated
LGTM
Reopening based on slack thread cc @Miraeld, @DahmaniAdame