Make WordPress Core

Opened 3 weeks ago

Last modified 5 days ago

#44302 new enhancement

X-DNS-Prefetch-Control default setting prevents resource hints from working for dubious 'security reasons'

Reported by: jonoaldersonwp Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Script Loader Keywords: 2nd-opinion has-patch needs-unit-tests
Focuses: performance Cc:



Since 4.6, WordPress automatically adds dns-prefetch tags into the wp_head block, for the hostname of any enqueued external resources.

Theme/plugin developers may add prefetch/similar resource hints.

These 'hints' allows the browser to initiate early DNS lookups, execute SSL handshakes, and apply similar optimisations to increase the efficiency of their loading processes.

Chrome(ium) ignores dns-prefetch over HTTPS

The most common (and automatically added) hint, dns-prefetch, is frequently ignored by browsers - rendering the dns-prefetch tags inert.

Specifically, the default behaviour of many browsers (Chromium-derived browsers, specifically) is to not allow dns-prefetch unless a X-DNS-Prefetch-Control HTTP header is set/detected.

X-DNS-Prefetch-Control is implied to be set to 'on' by default, but this excludes domains loaded over HTTPS, unless the value is explicitly set to on.

Because an increasing proportion of websites are migrating to / adopting HTTPS, we should have a stance and a solution for enabling (or, perhaps, removing) dns-prefetch tags.

Note that, there's no equivalent for Firefox / other browsers, other than end-user level configuration (e.g., in Firefox, setting network.dns.disablePrefetch to true).

It's a question of security

The argument boils down to, effectively, "Site owners of pre-fetched connections might be able to detect/monitor those connections".

More verbosely,

"Links with novel subdomains, when resolved during a prefetch, may notify a domain's resolver that a link was viewed, even if it was not clicked. In some such cases, the authority serving the content (such as a blog owner, or webmail server) may wish to preclude such abusive monitoring."

See 'DNS Prefetch Control' at http://dev.chromium.org/developers/design-documents/dns-prefetching

I believe that whilst the argument for avoiding DNS connection sniffing makes sense in a general context (although, it seems over-cautious), our use-case is much safer.

I'm struggling to envision many realistic risks or scenarios where this could be a meaningful attack vector, given that:

  • It'll be rare for a WordPress site to have 'unknown' DNS lookups; we're limited to enqueued external resources, and resource hints added by theme/plugin developers.
  • Rogue theme developers / site owners could manually add tags to their theme <head> markup, but by this point, they could be doing much worse stuff (intentionally or otherwise).

The proposal

We should detect whether resource hints are in play on a site, and if so, add a X-DNS-Prefetch-Control: on HTTP header.

This will result in a marked decrease to the loading time of WordPress sites which rely on third party resources (i.e., those which are enqueuing with resource hints), when loaded in a Chromium browser.

Optionally, we could restrict that behaviour to only trigger in the case that all enqueued resources / resource hints are from whitelisted CDNs (e.g., cdnjs.com, ajax.googleapis.com), which might have an added impact of encouraging CDN usage for common scripts/assets.

Thoughts appreciated. Apologies for any potential hiccups with categorisation of this ticket - first time I've raised a core issue/enhancement.

Attachments (1)

44302.diff (4.0 KB) - added by swissspidy 5 days ago.

Download all attachments as: .zip

Change History (2)

5 days ago

#1 @swissspidy
5 days ago

  • Keywords has-patch needs-unit-tests added

We should detect whether resource hints are in play on a site, and if so, add a X-DNS-Prefetch-Control: on HTTP header.

Makes sense I guess. 44302.diff is an untested patch that goes into that direction.

Also removes unnecessary "pr" and "as" attributes as per #42438.

Note: See TracTickets for help on using tickets.