2

I have this function in my functions.php file:

function dns_prefetch_to_preconnect( $urls, $relation_type ) {
    if ( 'dns-prefetch' === $relation_type ) {
        $urls = [];
    }

    if ( 'preconnect' === $relation_type ) {
        $urls = wp_dependencies_unique_hosts();
    }

    return $urls;
}
add_filter( 'wp_resource_hints', 'dns_prefetch_to_preconnect', 0, 2 );

It takes the URLs defined in wp_dependencies_unique_hosts() – which WordPress by default assigns to the dns-prefetch link tag – and reassigns them to the preconnect link tag. The function was provided to me here:

Change dns-prefetch to preconnect for external enqueued resources

However, this function isn't working entirely correctly. It adds the preconnect URLs using http instead of https.

Example: when I'm not using the above function, WordPress adds this link to my header:

<link rel='dns-prefetch' href='//fonts.googleapis.com' />

And when I enable the above function, it replaces that link with this link:

<link rel='preconnect' href='http://fonts.googleapis.com' />

The problem, of course, is that it should be https, not http.

Can someone help me modify my function so that it gives me https links?

  • My understanding is that the hosts returned by wp_dependencies_unique_hosts are just hosts, they aren't URLs and don't have a schema. Have you confirmed it is indeed http:// and that this isn't something the browser is adding? There should be no schema at all based on the code in your question – Tom J Nowell Jan 4 at 1:55
  • I tried your code and all that happened was the dns prefetch tags dissapeared, wp_dependencies_unique_hosts returned no hosts – Tom J Nowell Jan 4 at 2:43
  • Indeed I believe my suspicion is correct, if you pass a naked domain such as 'tomjn.com' it will prefix it with http:// as it doesn't know what the correct schema is, and it can't use https:// as the host may not support it. 'https://tomjn.com' however works as expected – Tom J Nowell Jan 4 at 2:46
  • Yes, you're quite right - wp_dependencies_unique_hosts is, indeed, just a list of hosts without any scheme. I agree with your conclusion that WordPress is adding 'http' as the scheme when the scheme isn't already there. – GermanKiwi Jan 6 at 23:03
1

The problem is not that the function you're using adds http:, the problem is it adds no URL schema at all!

As a result, WP needs to add a URL schema to turn the host into a URL, and so it uses http://. It has no way of knowing what the original was, or if the host supports HTTPS, so http:// is the safe bet.

If however you passed the array with URL schema added, it would be passed through without issue.

Something like this may do the trick:

$hosts = wp_dependencies_unique_hosts();
$urls = array_map( function( $host ) {
    return set_url_scheme( $host, 'https' );
}, $hosts );

In the longrun though, it would be better to get the actual URLs and distill the host URL out of them, rather than relying on wp_dependencies_unique_hosts if you wanted to preserve the mixture of http and https

| improve this answer | |
  • Thanks very much for your suggestion! Unfortunately, though, it didn't work for me when I added your snippet above to my existing function. My 'preconnect' links still began with http rather than https, but even worse, it also resulted in 'prefetch' and 'prerender' links being added for the same hosts! (And all of them just using http). However, your feedback got me onto the right track and I finally found a working solution, which I'll paste below in a new answer. – GermanKiwi Jan 6 at 23:06
  • hmmm all my snippet does is prefix the hosts array with https://, it's not a full replacement for your origiinal code, just an ammendment, so I don't see how it would make changes to prefetch or prerender – Tom J Nowell Jan 7 at 10:31
  • Yes, I know it's not a full replacement for my original function. I tried inserting your snippet into the beginning of my original function, where I figured it should work, but for whatever reason it didn't work (it's also possible I did something wrong there). In any case, I agree with your recommendation of finding a better solution for the long-run, which would get the actual URLs with their original schemes. Did you see the new function I posted in my other answer here, which seems to do the trick? Do you think it's the best way to do this? – GermanKiwi Jan 7 at 22:05
  • Ah, in that case it would return the hosts for all types of resource hints, even those that didn't have any URLs. It was only intended to go inside the preconnect if statement as a replacement for the line that got the hosts – Tom J Nowell Jan 8 at 0:35
  • Ok gotcha, that makes sense - it wasn't clear to me where exactly the snippet should go in the original function. Anyhow, as you noted, it forces https on all of the URLs regardless of whether the original URL used https or http, so it would only make sense to do that if you were certain that all of your external scripts/styles used https. As you haven't suggested any improvements to my other answer ;) I'm assuming it's correct as it is and I'll mark it as such. Thank you very much for your help and suggestions! – GermanKiwi Jan 8 at 8:18
0

I've finally got it working now, as follows:

First I found the original wp_dependencies_unique_hosts() function from the WordPress code (it's in a file called general-template.php), and I made a copy of it, but giving it a new name: wp_dependencies_unique_urls().

I observed that this function is using wp_parse_url() to grab just the host part of each URL from the list of scripts and styles. In other words, it's dropping the scheme, which is the reason for the problem I'm having.

So I modified the function to include the scheme - here it is in its entirety:

function wp_dependencies_unique_urls() {
    global $wp_scripts, $wp_styles;

    $unique_urls = array();

    foreach ( array( $wp_scripts, $wp_styles ) as $dependencies ) {
        if ( $dependencies instanceof WP_Dependencies && ! empty( $dependencies->queue ) ) {
            foreach ( $dependencies->queue as $handle ) {
                if ( ! isset( $dependencies->registered[ $handle ] ) ) {
                    continue;
                }

                $dependency = $dependencies->registered[ $handle ];
                $parsed     = wp_parse_url( $dependency->src );

                if ( ! empty( $parsed['host'] ) && ! in_array( $parsed['host'], $unique_urls ) && $parsed['host'] !== $_SERVER['SERVER_NAME'] ) {
                    $unique_urls[] = $parsed['scheme'] . '://' . $parsed['host'];
                }
            }
        }
    }

    return $unique_urls;
}

As you can see, the main thing I've changed is this:

$unique_urls[] = $parsed['scheme'] . '://' . $parsed['host'];

I hope this is the best way to add the scheme to the beginning of each URL.

Next, I modified my original function (from my original question above) so it calls this new function I've created:

function dns_prefetch_to_preconnect( $urls, $relation_type ) {

    if ( 'dns-prefetch' === $relation_type ) {
        $urls = [];
    }

    if ( 'preconnect' === $relation_type ) {
        $urls = wp_dependencies_unique_urls();
    }

    return $urls;
}
add_filter( 'wp_resource_hints', 'dns_prefetch_to_preconnect', 0, 2 );

Et voila, it works! I now have valid 'preconnect' links in my page headers, which use the same scheme as the original enqueued scripts and styles - either http or https!

And if I want to, I can combine my two functions into one big function for simplicity:

function dns_prefetch_to_preconnect( $urls, $relation_type ) {
    global $wp_scripts, $wp_styles;

    $unique_urls = array();

    foreach ( array( $wp_scripts, $wp_styles ) as $dependencies ) {
        if ( $dependencies instanceof WP_Dependencies && ! empty( $dependencies->queue ) ) {
            foreach ( $dependencies->queue as $handle ) {
                if ( ! isset( $dependencies->registered[ $handle ] ) ) {
                    continue;
                }

                $dependency = $dependencies->registered[ $handle ];
                $parsed     = wp_parse_url( $dependency->src );

                if ( ! empty( $parsed['host'] ) && ! in_array( $parsed['host'], $unique_urls ) && $parsed['host'] !== $_SERVER['SERVER_NAME'] ) {
                    $unique_urls[] = $parsed['scheme'] . '://' . $parsed['host'];
                }
            }
        }
    }

    if ( 'dns-prefetch' === $relation_type ) {
        $urls = [];
    }

    if ( 'preconnect' === $relation_type ) {
        $urls = $unique_urls;
    }

    return $urls;
}
add_filter( 'wp_resource_hints', 'dns_prefetch_to_preconnect', 0, 2 );
| improve this answer | |
  • @tom-j-nowell what do you think? Have I found the best solution to the problem? Are there any improvements you can suggest for my new function? – GermanKiwi Jan 6 at 23:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.