Code Monkey home page Code Monkey logo

Comments (142)

trajano avatar trajano commented on May 25, 2024 3

Here's a slightly better one that I am using, it will scan through and preload all JS, CSS PNG and JPGs that are found in the content. I kind of want to remove the second regexp, but got lazy :)


                        $header = "Link: ";
                        $regexp = '#(src|href)="([^"]+\.(js|css|png|jpg)(\?[^"]+)?)"#';
                        if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                                foreach ($matches as $match) {
                                        $file = $match[2];
                                        $type = $match[3];
                                        if ($type === 'js') {
                                                $type = 'script';
                                        } else if ($type === 'css') {
                                                $type = 'style';
                                        } else {
                                                $type = 'image';
                                        }
                                        $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                                }
                        }
                        $regexp = str_replace('"', "'", $regexp);
                        if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                                foreach ($matches as $match) {
                                        $file = $match[2];
                                        $type = $match[3];
                                        if ($type === 'js') {
                                                $type = 'script';
                                        } else if ($type === 'css') {
                                                $type = 'style';
                                        } else {
                                                $type = 'image';
                                        }
                                        $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                                }
                        }
                        header(rtrim($header, ","));

from autoptimize.

futtta avatar futtta commented on May 25, 2024 3

although I'm mainly working on stablizing AO 2.2 (hope some of you have it running, need to be more or less confident it's OK before releasing), this code snippet (a proof of concept, nothing more) pushes the AO'ed files and jquery.js;

add_filter('autoptimize_filter_cache_getname','pushAOFiles');
function pushAOFiles($in) {
  $pushType = substr($in,strrpos($in,".")+1) === "js" ? "script" : "style"; 
  header('link: <'.$in.'>; rel=preload; type='.$pushType,false);
  return $in;
}

add_filter('autoptimize_filter_js_exclude','pushJQuery');
function pushJQuery($in) {
	if (strpos($in,"js/jquery/jquery.js")!==false) {
	  	$jQurl=includes_url("js/jquery/jquery.js");
	    header('link: <'.$jQurl.'>; rel=preload; type=js',false);
	}
}

I think that if we're HTTP/2 pushing, we would also want to remove the defer flag from the autoptimized JS, OR go for "look only in head"+"force in head" for JS.

from autoptimize.

zytzagoo avatar zytzagoo commented on May 25, 2024 2

Some interesting details here: https://blog.yoav.ws/being_pushy/

from autoptimize.

trajano avatar trajano commented on May 25, 2024 2

Can

$content = apply_filters( 'autoptimize_html_after_minify', $content );
be changed to pass in the URL for the cached + minified CSS and JS? That way we can do

function http2_server_push($content, $cached_js, $cached_css) {
  header(
    sprintf(
      'Link: <%s>; rel=preload; as=%s',
         $cached_js, 'script'
    )
  )
  header(
    sprintf(
      'Link: <%s>; rel=preload; as=%s',
         $cached_css, 'style'
    )
  )
}
add_filter('autoptimize_html_after_minify', 'http2_server_push')

from autoptimize.

trajano avatar trajano commented on May 25, 2024 2

Anyway the workaround I have for now is

function http2_server_push($content) {
  $header = "Link: ";
  if (preg_match('#="([^"]+/js/autoptimize_[0-9a-f]+\.js)"#', $content, $matches)) {
    $header .= sprintf(
        '<%s>; rel=preload; as=%s,',
           $matches[1], 'script'
      );
  }
  if (preg_match('#="([^"]+/css/autoptimize_[0-9a-f]+\.css)"#', $content, $matches)) {
    $header .=
      sprintf(
        '<%s>; rel=preload; as=%s',
           $matches[1], 'style'
      );
  }
  header($header);
  return $content;
}
add_filter('autoptimize_html_after_minify', 'http2_server_push');

Seems to work a bit on my blog https://www.trajano.net/. I see the JS, CSS being loaded as soon as possible when I check the network graph.

from autoptimize.

PatTheMav avatar PatTheMav commented on May 25, 2024 2

FYI (and just because I haven't seen it being mentioned) - the Cloudflare plugin supports HTTP/2 push (activated using a wp-config directive) and adds headers and <link rel="preload" as="script|style"> entries in the <head> - of course it doesn't do any good with autoptimize enabled, but at least gleaning at the source might be of interest.. πŸ™‚

from autoptimize.

tdtgit avatar tdtgit commented on May 25, 2024 2

The below function works with latest AO even.

function http2_server_push($content) {
	$header = "Link: ";
	  if (preg_match('#="([^"]+/js/autoptimize_[0-9a-f]+\.js)"#', $content, $matches)) {
	    $header .= sprintf(
	        '<%s>; rel=preload; as=%s,',
	           $matches[1], 'script'
	      );
	  }
		
	  if (preg_match('#="([^"]+/css/autoptimize_[0-9a-f]+\.css)"#', $content, $matches)) {
	    $header .=
	      sprintf(
	        '<%s>; rel=preload; as=%s',
	           $matches[1], 'style'
	      );
	  }
	  header($header);
	  return $content;
}
add_filter('autoptimize_html_after_minify', 'http2_server_push');

from autoptimize.

WebSwiftSEO avatar WebSwiftSEO commented on May 25, 2024 1

If you guys need anything, just send me a Short message and I can Look into
it :-) ;-)

On Friday, 12 August 2016, 2ACES [email protected] wrote:

@zytzagoo https://github.com/zytzagoo interesting article indeed

β€”
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#49 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ARjhk8fr6H0knFE1nvjsvAxHR3qruc04ks5qe5cHgaJpZM4JRNhx
.

Best Regards

Ylia Callan

WEB SWIFT SEO
Tips - Tools - Techniques

https://webswiftseo.com

from autoptimize.

futtta avatar futtta commented on May 25, 2024 1

well, I think at the very least we should hook into wordpress' send_headers action hook to avoid sending headers out of order.

from autoptimize.

trajano avatar trajano commented on May 25, 2024 1

I had a recent finding with HTTP2 Server Push, but it may just be a Chome bug/limitation. https://trajano.net/2017/01/double-downloads-with-http2-server-push/ if you preload and the resource is not dynamically added using scripts in Chrome you will get a double download.

However, given that we can probably do an optimization where the CSS is added to the DOM by the script and asynchronously loaded. With HTTP/2 Server Push the CSS can be preloaded in the background while the initial DOM is being processed and then bound later by the script.

from autoptimize.

futtta avatar futtta commented on May 25, 2024 1

re. double downloads; sounds vaguely related to filamentgroup/loadCSS#110

from autoptimize.

futtta avatar futtta commented on May 25, 2024 1

i never defer jquery.js, in ao's default config it's even excluded from optimization. the point i was trying to make is that we might want to http/2-push jquery.js though :)

from autoptimize.

zytzagoo avatar zytzagoo commented on May 25, 2024 1

Required reading: https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/

from autoptimize.

zytzagoo avatar zytzagoo commented on May 25, 2024 1

My (simple) takeaway is to keep things on 1.1 still for now, it's pretty messy... and things can actually end up being slower.

But, really, the article is a must read. Especially about the preload/push distinction.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

absolutely! :-)

but does this work when using page cache plugin and when AO is set to deliver static files? because in that case you might not have any PHP to start with?

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

All tests were done on WPengine (which uses a custom varnish implementation, I think) with and without Cloudflare. I guess their cache keeps the headers sent by the original PHP page in the static version.

I will check on other hosts and let you know the results ASAP.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

So I read up on the topic a bit :-) and found this interesting article on smashing mag. about preloading, which focuses primarily on the HTML-based preloading (but also mentions the HTTP header-approach).

I think adding the preload both as HTTP resp. header and in the HTML (link rel=preload) would ensure that even in a fully static setup (where headers are not cached) the resources would be loaded over the existing HTTP/2 connection (@daveros does the same, actually)?

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

TL;DR

  • That's the approach we use on our production sites and apps. It should work with AO.
  • check both specifications drafts for preload and other resource hints and Ilya Grigorik notes on the subject.

The Long Version (food for thought)

So far, we use a custom plugin to each site, aggregating all functions, AO filters and techniques for optimization, including different resources hints ( https://www.w3.org/TR/resource-hints/ ) for different resources to ensure maximum browser support, depending on the resource importance, origin, position and how sure we are we will use a given resource:

    <link rel="preconnect" href="https://api.tiles.mapbox.com">
    <link rel="dns-prefetch" href="https://api.tiles.mapbox.com">
    <link rel="preload" as="script" crossorigin href="https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js">
    <link rel="preconnect" href="https://api.mapbox.com/">
    <link rel="dns-prefetch" href="https://api.mapbox.com/">
    <link rel="preload" as="style" href="https://mydomain.com/path-to-ao-cache/ao-aggregated-style.css">
  • Line 1: we know we need to connect to that domain, but not exactly which resource. We use preconnect to suggest browsers which support it to initiate connection.
  • Line 2: this hint has a wider browser support, we use as fallback for browsers that don't support the previous hint to make the initial DNS lookup. In our tests, browsers supporting preconnect ignore this one if the connection on line 1 was made (all resource hints are suggestions... er, hints... and UA decides if it will process it) .
  • Line 3: We know exactly which resource we want and it's really important. Let's preload it in browsers that support it. Note that we need both the crossorigin andas parameters if said resource is external.
  • Lines 4 and 5: act as fallback for browsers not supporting preload, same way like lines 1 and 2.
  • Line 6: we know we will need this ΓΌber important resource, lazy-loaded by AO. So, let's fetch it.

Since last week, we're shipping our plugins with our send header functions just with preload for the most important resources. That's what "triggered" our suggestion, because we thought it would be great for other AO users. :-)

I guess AO would work great with just preload on headers and inline, but if you want to have wider browser support, use preconnect and dns-prefetch as well. This should be filterable/optional because it will work great most of time, but sometimes it won't, depending on the aggregated file size, original CSS rules, dom complexity, etc

from autoptimize.

futtta avatar futtta commented on May 25, 2024

great work, interesting stuff! looking forward to your contributions!!

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

@zytzagoo interesting article indeed

from autoptimize.

futtta avatar futtta commented on May 25, 2024

cfr. https://wordpress.org/support/topic/adding-preload-to-the-header?replies=2

from autoptimize.

futtta avatar futtta commented on May 25, 2024

Nice! It does work; your AO JS is linked at the end of the HTML, with defer attribute, but it is indeed loaded immediately as per this webpagetest.org test which also shows that the initial request has this as header;

link: <https://www.trajano.net/wp-content/cache/autoptimize/js/autoptimize_7f1fb7f2c06f6c4218428fe4c1904176.js>; rel=preload; as=script,<https://www.trajano.net/wp-content/cache/autoptimize/css/autoptimize_dc58df74105fec34d124e8ddef6f0210.css>; rel=preload; as=style

the only thing which I can't deduct from the waterfall chart is that the preloaded CSS/ JS isn't render-blocking (it shouldn't as per the specs, obviously).

If I were you I would install a page cache plugin to minimize my TTFB, would be interesting to see if such a plugin also caches headers?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

I would agree with the page cache. I just never got around to doing it, I've tried a few before (like years ago) but they had some issues on a very limited memory machine and running on Oracle Linux with SELinux on full blast. It may be better now, but I never invested time on it.

However for one thing I don't like having to wait on the cache, I would rather it change as I change things. Since my blog is more for play around rather than a heavily utilized site.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

What I would like to know is if it were at all possible to send the headers ASAP then the content because it looks like it processes the whole page first. But then a cache would likely help there.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

it simply has to process the page first, as:

  • headers are sent as part of the actual HTTP response
  • autoptimize needs the full page to extract & optimize the CSS/JS and you need AO to extract the to-be-preloaded URL's

so yeah, I would go the page cache route :-)

from autoptimize.

trajano avatar trajano commented on May 25, 2024

Tried using WP Super Cache (had it's share of issues with permissions and what not) but I got it working in the end. I lose the headers now :(

Content is faster though.

https://www.webpagetest.org/result/160821_V0_F2D/2/performance_optimization/#first_byte_time

from autoptimize.

futtta avatar futtta commented on May 25, 2024

I guess (hope) there must be page caching plugins that also cache headers ...

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

@2aces

There are two preloads around and it can cause confusion.

The W3 link you linked is not a push preload. It is just an instruction to the browser to fetch a resource with highest priority (and not execute it or use a code onload).

This can be done even if there is no HTTP2.

The other preload is of course pushing assets to the browser.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

For server push you need two things: the Link header and a server such as nghttp2 that can parse the Link header and start sending. I haven't gotten it to work with nginx yet.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

Some thoughts:

Push optimisation is the best when the critical css is pushed and the html doesn't have any inline/critical css. It's a separate file.

Pull preload for the full css file will be as good as the push css.

It's pushing the critical css which can make the difference.

As Ilya Grigorik says:

n fact, if you have ever inlined a resource (CSS, JS, or an image), you've been "simulating" server push: an inlined resource is "pushed" as part of the parent document. The only difference is that HTTP 2.0 makes this pattern more efficient and far more powerful! ... HTTP 2.0 server push obsoletes inlining.

https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/

So if it's something to be pushed ... it's the critical css, not the full css.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

In other words, the best optimization is:

  • critical css is not inlined and pushed as an external css.
  • the html has a render blocking link declaration for the critical css.
  • the full css is fetched via a pull preload, not pushed.

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

Just to give some sanity check, we are not talking about only HTTP2 push header anymore, right? If so, maybe we should change the Issue title and description.

from autoptimize.

2aces avatar 2aces commented on May 25, 2024
  1. we gotta evaluate which resources to support;
  2. we gotta evaluate which methods will be available (link element, http2 push);
  3. decide if we use WordPress 4.6 class or write our own;
  4. which ones will be available on settings and which ones only by filters;

What is sure is that every setup will have different demands and outcomes. I mean:

  • 1 page sites VS sites with lot of pages VS sites with something in between have different demands?
  • Theme templates which are almost the same VS too diferent post type and archives templates.

Specifically about preload AND HTTP2 push, we gotta be careful as @zytzagoo pointed out, it may result in overhead for subsequent page visits . In my specific test setups on WPEngine with sites with small diferences, it was worth anyway.

Build on what @vijayaraghavanramanan listed as the best optimization:

  • critical css is not inlined and pushed as an external css (1)
  • the html has a render blocking link declaration for the critical css (2)(3)
  • the full css is fetched via a pull preload, not pushed (4).
    - the full css is inserted on DOM dynamically as is done today when using inline CSS(5)
  • (1) optionally, via filter, keeping backwards compatibility and non-advanced users sanity).
  • (2) optionally as effect of the previous one.
  • (3) why not pushing it, specially when using an CDN, aiming to avoid blocking for long times while waiting the DNS lookup and download. After all this is the critical one and should be parsed and applied ASAP.
  • (4) why not an option of doing both, via filter?
  • (5) some browsers don't support preload and some servers won't have HTTP2 headers push support, and even they do, it may take some time to download the CSS, so, loading dynamically will keep users happy any way.

PS: @trajano your code looks efficient for this, as soon as I am able, I will test it.

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

About using WordPress class: it doesn't support preload right now. I think the best course of action would be expanding it for preload and HTTP2 push headers and if works good, we propose it to merge it on core.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

@2aces,

Correct.

Only Chrome/Opera support preload in stable and Firefox is building it but not in Nightly yet. My points were incomplete.

So I should say my points should read:

  • critical css is not inlined and pushed as an external css.
  • the html has a render blocking link declaration for the critical css.
  • the full css is fetched via a pull preload, not pushed and applied via "onload" in the link rel declaration.
  • for browsers which do not support preload, polyfill it with Autoptimize's existing js.
  • for these browsers also use a sessionstorage variable to optimize on second load, i.e, load the full css immediately in the head on repeat website view.

I mentioned not pushing the full css as one should push as less as possible. So html and critical css can load fast if that's the case.

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

@vijayaraghavanramanan :

"for browsers which do not support preload, polyfill it with Autoptimize's existing js."
My understanding of preload draft specification and for all tests I conducted is that pushing the CSS using http2 push doesn't mean it is inserted in the DOM and parsed, it is only downloaded. Therefore, we need AO javascript even when the browser and server supports pushing headers.

"for these browsers also use a sessionstorage variable to optimize on second load, i.e, load the full css immediately in the head on repeat website view."
Can you elaborate?

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

@2aces

Right. It's just downloaded. But it allows what you can do onload. So that's why I said onload in point 3 in my previous comment.

So you can declare it like this:

<link rel="preload" href="http://www.example.com/wp-content/cache/autoptimize/css/autoptimize-hash.css" as="style" onload="preloadFinished(this)">

and before it define a function in javascript

<script>
function preloadFinished( el ) {

...

}
</script>

or pass href instead of this

About sessionstorage, what I meant was that once a visitor visits a site the css file is already in the cache. So for repeat views, you can do better than loading css in the footer. The browser now doesn't need to fetch the css from the server as it is in the cache.

So in the footer, add this line in javascript:

sessionStorage.fullaocssloaded = "true";

And in the header,

<script>
if (! relpreloadsupport) {
  if (sessionStorage.fullaocssloaded ) {
     //javascript to insert the full css immediately. 
 }
}
</script>

What the above code does is that it checks if there's a sessionStorage variable. If true, it means almost certainly that the css is in the cache. So why not load it in the head.

It's slightly more complicated as some browsers do not allow sessionStorage in incognito mode.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

This is the full javascript code. It use requestAnimationFrame, but you can use Autoptimize's lCSS instead.

The code is a collection of various snippets at various points in the HTML, not to be used next to each other.

function preloadFinished(node) {
    var res = document.createElement("link");
    res.rel = "stylesheet";
    res.href = node.href;
    node.parentNode.insertBefore( res, node.nextSibling );
}

var linkSupportsPreload = function() {
    try {
        return document.createElement("link").relList.supports("preload");
    } catch (e) {
        return false;
    }
};

var sessionStorageAvailable = function() {
    var mod = 'modernizr';
    try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
    } catch (e) {
        return false;
    }
};

var cb = function() {
    var links = document.getElementsByTagName("link");
    for (var i = 0; i < links.length; i++ ) {
        var link = links[i];
        if( link.rel === "preload" && link.getAttribute( "as" ) === "style" ) {
            preloadFinished(link);
        }
    }
}

if( !linkSupportsPreload() ) {
    if( (sessionStorageAvailable() && sessionStorage.fullcssloaded) || !sessionStorageAvailable() ) {
        cb();
    }
}

var rAF = (function() {
    return window.requestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || window.webkitRequestAnimationFrame || function( callback ) {
        window.setTimeout(callback, 1000 / 60);
    }
})();

if ( !linkSupportsPreload() ) {
    if( sessionStorageAvailable() && !sessionStorage.fullcssloaded ) {
        setTimeout(function() {
            rAF(cb);
            sessionStorage.fullcssloaded = "true";
        });
    }
}

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

Btw in select cases, it makes sense to just have the full css pushed and block render with the full css and not have critical/above the fold css at all.

It won't have the problems of speed as the css arrives at the same time as the initial HTML, so rendering is fast as the usual "inline and defer"

But that's in the case where the full css is not a big file. If it's big, it makes sense to push the critical css file only.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

I have a slightly improved optimization method below. The simplest solution is of course is to push the full css. But this is not optimal as its size can be greater than 14KB. Some Wordpress bundled themes have things such as Genericons which are huge and its not optimal to push them.

My improvement to the previous comments is that for second loads, you don't need critical css. The server might still push it for second loads (but some are thinking of improvements to cancel it from the client side) and it's still better to not use it and thus avoid repaints.

  • Push critical.min.css and insert it synchronously via javascript depending upon whether the page has a sessionstorage variable. (My earlier comment said insert it via link). If the page doesn't have sessionstorage variable set to true, insert it synchronously, else don't use critical.min.css. Effectively first load uses critical.min.css but second load doesn't).
  • Fetch the full style.min.css via pull preload (not push) and apply it onload if the page doesn't have sessionstorage variable. If the page has sessionstorage variable set to true, don't pull preload and instead insert style.min.css via javascript synchronously.
  • For browsers not supporting pull preload, polyfill (the second point).

(Browsers which don't support HTTP2 push see very old behaviour, don't enjoy "inline and defer". Minor sacrifice since most modern browsers do support HTTP2 push).

from autoptimize.

futtta avatar futtta commented on May 25, 2024

My 2c:

  • as HTTP/2 support doesn't only depend on client but also server and as I want AO support to be as broad as possible (and as I like clean and simple solutions), I would by default (so this could be different behind a filter, but that filter would for now default to false) keep inline CSS inline, also for 2nd loads, the overhead is minimal and this is never render-blocking (so only disadvantage is slightly bigger HTML payload).
  • the pull preload solution is fancy, but rather complex esp. with that polyfilling going on (e.g. I don't want to depend on modernizr in AO)
  • push is the future

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

Hi Frank,

Yeah, It's just catching up and would imagine only few percent of server fully supporting it. This is because in addition to supporting push, they also need to be HTTPS. Push can work without HTTPS but browsers have implemented it so that it can work only in HTTPS. So inlining critical css as AO default makes sense.

About my code, I took it from Modernizr but doesn't depend on it. So alternatively one can use this from Mozilla's site:

function storageAvailable(type) {
    try {
        var storage = window[type],
            x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
    }
    catch(e) {
        return false;
    }
}

if (storageAvailable('localStorage')) {
    // Yippee! We can use localStorage awesomeness
}
else {
    // Too bad, no localStorage for us
}

with sessionStorage instead of localStorage.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

weird that you got

The resource … was preloaded using link preload

while you were not preloading via a link but via the HTTP response header? or were you doing both?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

Either one will yield the same problem. Doing in Link header will just download a bit more data sooner I presume because of Server Side push. Again I think it could be a Chrome implementation issue, because I do not see anything in the spec that states that it needs to be loaded via script. But then again it could be worded differently.

from autoptimize.

superpoincare avatar superpoincare commented on May 25, 2024

I have seen those sort of Chrome warnings but it's usually because something is not done right.

This is a good page and I don't get any warning here on Chrome:

https://www.filamentgroup.com/lab/modernizing-delivery.html

from autoptimize.

henriqueccruz avatar henriqueccruz commented on May 25, 2024

@trajano
Very interesting approach, I want to use it.
Would you share the most recent version of your code?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

@vijayaraghavanramanan i think it a proper way to validate is to find out whether the a resource was sent via H2 AND downloaded as part of the webpage request either via Link header or some other way. By doing the H2PushResource it may tell Apache to explicitly send the resource over the wire but they could just be sent without being attached and redownloaded again.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

I don't have a updated to the latest version of Cache-Enabler, I did a diff and the key functions are below for wp-content/plugins/cache-enabler/inc/cache_enabler_disk.class.php

private static function endsWith($haystack, $needle)
{
            $length = strlen($needle);
            return (substr($haystack, -$length) === $needle);
}
private static function _link_header($uncompressed_file_data) {
        $header = "";
                $regexp = "#'((https?:)//[^']+/[^/']+\.js(\?[^']+)?)'#";
                 if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                         foreach ($matches as $match) {
                                 $file = $match[1];
-
                                 $type = 'script';
                                 $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                         }
                 }
                 $regexp = str_replace("'", '"', $regexp);
                 if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                         foreach ($matches as $match) {
                                 $file = $match[1];
                                if (self::endsWith($file, '/html5.js')) {
                                        continue;
                                }
                                 $type = 'script';
                                 $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                         }
                 }
                $regexp = '#(src|href)="([^"]+\.(css|png|jpg)(\?[^"]+)?)"#';
                 if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                         foreach ($matches as $match) {
                                 $file = $match[2];
                                 $type = $match[3];
                                 if ($type === 'css') {
                                         $type = 'style';
                                 } else {
                                         $type = 'image';
                                 }
                                 $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                         }
                 }
                 $regexp = str_replace('"', "'", $regexp);
                 if (preg_match_all($regexp, $uncompressed_file_data, $matches, PREG_SET_ORDER)) {
                         foreach ($matches as $match) {
                                 $file = $match[2];
                                 $type = $match[3];
                                 if ($type === 'css') {
                                         $type = 'style';
                                 } else {
                                         $type = 'image';
                                 }
                                 $header .= sprintf('<%s>; rel=preload; as=%s,', $file, $type);
                         }
                 }
                 return rtrim($header, ",");
}

from autoptimize.

futtta avatar futtta commented on May 25, 2024

So open question; is HTTP2-pushing (almost) all resources (js, css, images) a good idea? Or should one rather push those resources that are needed for the initial page rendering?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

If you can somehow push those that are part of the theme (i.e. scripts, CSS) that would be better. However, they will only work when the data is loaded via script rather than part of the source. https://trajano.net/2017/01/double-downloads-with-http2-server-push/

If HTTP/2 is going to be enabled somehow it should defer the scripts and CSS

I am not sure if that double downloads issue is fixed on the current versions on Chrome though.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

Another approach I can think of is to NOT do anything automatically to determine what should be pushed. Instead let the theme or blog developer do it themselves by having <link rel="preload" ... tags in their content. These tags will then be parsed and stripped off so it will be sent via the HTTP Headers instead. The purpose of having them on HTTP Headers rather than the content is for HTTP2 Server Push compatible servers it should start sending the data ASAP before the client had requested it.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

How is support for HTTP/2 push now, which servers support that out of the box?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

@trajano you don't seem to HTTP2-push on your website (cfr. https://www.webpagetest.org/result/170404_BD_E26/1/details/#waterfall_view_step1)?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

Not anymore, I found that it didn't work too well in Chrome. It was actually causing double downloads.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

The main problem is Chrome will only "link" the content if it was built from a script rather than the HTML. So <img src=... /> will double download but setting .src via JavaScript will not.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

I still preload a few things on my main portfolio https://trajano.net/ however, since they're all remote resources (i.e. google fonts etc) it won't be server push. Only those that are authorative can be server push.

from autoptimize.

trajano avatar trajano commented on May 25, 2024

Many issues... it may not be fixed though unless the spec has made it explicit that it should also be for non-scripted resources. So far only script injected resources will not be double downloaded.

https://bugs.chromium.org/p/chromium/issues/detail?id=655698
ampproject/amphtml#7076

from autoptimize.

futtta avatar futtta commented on May 25, 2024

more in:
https://bugs.chromium.org/p/chromium/issues/detail?id=655698
w3c/preload#80

But these focus on a correct as= attrib. missing, which was not the case for you was it @trajano ?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

I think it is more that it is dismissed already regardless I found that we shouldn't try to do guess what needs to be sent automatically and instead rely on the actual theme developer (which is why I stated use the resulting data. Stripping it off and sending it as part of the header would have some benefit HTTP2 will package the header more efficiently.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

Well, for a site using AO (which is our context after all) I think HTTP2-pushing the autoptimized CSS & JS (and jquery, if excluded) could make sense actually :-)

from autoptimize.

trajano avatar trajano commented on May 25, 2024

from autoptimize.

futtta avatar futtta commented on May 25, 2024

You cannot push jquery unless you defer loading jquery.

that part I didn't understand. is that the bug/ limitation you stumbled upon, or just as the spec intented?

However the interim of doing the link parsing will help.

what do you mean by that? harvesting link's in the header looking for preload and pushing those? doesn't really apply in AO's context, as AO aggregates all linked CSS replacing it with between 1 and 3 (generally, depends on different media-attribs in the original links) files.

configure the server automatically

must be getting tired, but I don't understand that part either, would you care to elaborate?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

from autoptimize.

trajano avatar trajano commented on May 25, 2024

I just created a Gist, the warnings does appear in Chrome 57 http://rawgit.com/trajano/a61654502d4aa6191d742766f87dc5fe/raw/4aaa942f8457513d60fd1a26d953fd847b2aaea2/preload-double-download.html

from autoptimize.

futtta avatar futtta commented on May 25, 2024

Indeed, but that's for in-HTML link-preload, question is if this would also (still) happen when (only) doing HTTP/2 push (i.e. in HTTP response header).

from autoptimize.

2aces avatar 2aces commented on May 25, 2024

Just pinging here to 1 - apologize for not being active in the discussion/development because of health and other issues 2- fantastic collaboration in this post/feature, guys.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

@2aces: get better soon!

@PatTheMav : wonder if combining HTTP/2 push (i.e. link preload in HTTP response headers) and link rel=preload in HTML <head> would not result in unwanted behavior? Have you been able to test this?

from autoptimize.

PatTheMav avatar PatTheMav commented on May 25, 2024

@futtta - not yet, as I'm primarily using Safari, so I'd have to check this in my Windows VM on Edge.. πŸ˜‰

But their docs make it seem like it's a "enable & forget" feature:

https://support.cloudflare.com/hc/en-us/articles/115002816808-How-do-I-enable-HTTP-2-Server-Push-in-WordPress

The source code itself mentions the following about adding the <link> elements:

Render "resource hints" in the section of the page. These encourage preload/prefetch behavior when HTTP/2 support is lacking.

Also it's just adding the headers using PHP's header function after adding itself as a filter for the script_loader_src and style_loader_src events. In addition it's mentioning an 8kb header size limit for Cloudflare (and a 4kb header size limit for fastcgi) so it limits the size of the header field to 3072 bytes (including CLRFs).

I'm not too versed with Wordpress' innards, so I don't know if there is a more elegant way to pull this off, but it seems workable.

Edit: Of course there is the elephant-in-the-room question about jQuery and many themes' reliance on jQuery being fully loaded in the head.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

@PatTheMav I think we would indeed need to HTTP/2-push jquery.js, yes. we'll also have to (re-)consider deferring JS. if HTTP/2-pushed, the JS would not render-blocking (although the execution might be, cfr. "async"-flag which makes downloading not render-blocking but the actual execution is) so deferring (which delays JS execution until very late in the process) might not be optimal.

options options options ... ;-)

from autoptimize.

trajano avatar trajano commented on May 25, 2024

from autoptimize.

PatTheMav avatar PatTheMav commented on May 25, 2024

@trajano - I'd say it's common practice to exclude jQuery from any deferred loading (and is more or less the default in AO). But @futtta is right that HTTP/2 push might make deferring itself (or placing in the footer) obsolete.

Personally I have no idea how the JS execution stack works in that case though, especially in conjunction with the event loop. There's a reason why some jS still "wants" to be loaded and parsed in the <head>, also some JS makes quite "optimistic" assumptions about when and how it's being loaded.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

regarding cloudflare; relevant code is here

regarding article @zytzagoo shared; interesting they make a distiinction between push and preload, as (based on what I read) webservers also use the preload http response header to determine what to push?

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

I went through all this topic a few months ago and now. I have been using the code based on trajano's comment on 21 Aug 2016. It has been spitting quite a big header. I have removed the images and focused only on css and js and then I had (among others) such headers sent:
<//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>; rel=preload; as=script,<//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>; rel=preload; as=script,<//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>
... and so on.

The new code from futta's comment on on 31 May 2017 displays only AO files.

Does the push specification allow for 3rd party files in the headers? Does it make sense at all?

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

I took a look at what other popular sites are doing with server push and I noticed in the headers sent by akamai.com:

Link: https://www.googletagmanager.com;rel="preconnect",https://www.google-analytics.com;rel="preconnect",https://www.googleadservices.com;rel="preconnect"

So maybe it is not so bad idea? Is there any way to get also other js files by some smart filters in AO 2.2?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

well, preconnect is not HTTP/2 push, it's "just" telling the browser to already create a HTTP-connection to those domains :-)

regarding how many files to HTTP/2 push; only time (and testing) will tell, but from what I've read, heard & discussed one would not want to push all files, but limit the push to those files that are needed for the immediate rendering and leave the rest to normal loading?

from autoptimize.

trajano avatar trajano commented on May 25, 2024

from autoptimize.

kevin25 avatar kevin25 commented on May 25, 2024

Do you have idea about this warning? Looks like the push server doesn't work out.

The resource /wp-content/themes/flatsome/assets/css/fl-icons.css?ver=3.3 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing.
08:59:09.128 (index):1 The resource /wp-content/themes/flatsome/assets/css/flatsome.css?ver=3.3.8 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing.
08:59:09.128 (index):1 The resource /wp-content/themes/flatsome-child/style.css?ver=3.3.8 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing.
08:59:09.128 (index):1 The resource /wp-content/themes/flatsome/assets/css/flatsome-shop.css?ver=3.3.8 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

@kevin25 see some of @trajano's replies above; looks like chrome will (re-)download HTTP2-pushed resources if those resources aren't added by JS, so preloading normally linked CSS will probably see Chrome consider those preloaded files as useless.

from autoptimize.

kevin25 avatar kevin25 commented on May 25, 2024

@futtta So any solution for this?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

from autoptimize.

trajano avatar trajano commented on May 25, 2024

You just need to know what the scripts would load (which is not really an easy thing to do in a general automated fashion)

Some candidates for this would be local fonts which can passed using preload and HTTP Push. Below-the-fold CSS can also be preloaded while the above the fold CSS is still part of the HTML that gets originally sent.

Lastly another candidate for preload would be the Ad network CSSes which are generally loaded via script.

In order for this to work well you need to have them part of the header rather than the body because if it is in the body then it will have to read a few bytes in and parse before it can do anything. You still need it in the body to actually be used by your page though.

The hack I did for this on an earlier version was to create a ".headers" file that gets sent if it is found and have a rule in the Apache configuration.

from autoptimize.

kevin25 avatar kevin25 commented on May 25, 2024

@futtta Do you have any sample?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

from autoptimize.

trajano avatar trajano commented on May 25, 2024

@kevin25 http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml but I don't recommend it for anything above the fold. (i.e. anything shown on the first page before you scroll)

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

I think the solutions in this post stopped working.
I have:

add_filter('autoptimize_filter_cache_getname','pushAOFiles');
function pushAOFiles($in) {
$pushType = substr($in,strrpos($in,".")+1) === "js" ? "script" : "style";
header('Link: <'.$in.'>; rel=preload; type='.$pushType,false);
return $in;
}
add_filter('autoptimize_filter_js_exclude','pushJQuery');
function pushJQuery($in) {
if (strpos($in,"js/jquery/jquery.js")!==false) {
$jQurl=includes_url("js/jquery/jquery.js");
header('Link: <'.$jQurl.'>; rel=preload; type=js',false);
}
}

and it seems it doesn't work anymore. Any help with that highly appreciated.

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

Indeed, but it is sooo heavy! I am looking for a more light solution.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

what version are you testing against @grzegorz-janoszka ; 2.3.4 or 2.4-beta ?

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

Ah, indeed that's handy information. I am using 2.3.4 still.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

just tested on my local dev machine, works for me @grzegorz-janoszka ?

image

from autoptimize.

tdtgit avatar tdtgit commented on May 25, 2024

Hi @futtta, just wonder your preload script using type instead of as.

Timeline of using as, scripts and styles preloaded at top priority:
screen shot 2018-04-19 at 10 34 26

Timeline of using type, CSS files are not loaded even:
screen shot 2018-04-19 at 10 35 27

And maybe

header('link: <'.$jQurl.'>; rel=preload; type=js',false);

should be

header('link: <'.$jQurl.'>; rel=preload; as=script',false); too :)

from autoptimize.

futtta avatar futtta commented on May 25, 2024

absolutely @tdtgit :)

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

@futta, thank you for testing. I noticed in the past that it sometimes worked, sometimes didn't. I checked all the headers called with Link: and they all have false as the second argument.
I was thinking that maybe wordpress already output some data and thus the header call is void.
I have another header call that always worked and it is at the 'template_redirect' level.
What main level of actions/filters is the code I pasted called?

from autoptimize.

tdtgit avatar tdtgit commented on May 25, 2024

@grzegorz-janoszka Try my edited code here. @futta's code is running fine when adding Link preload to response header, but not correctly so browser isn't preloading resources at top priority.

from autoptimize.

futtta avatar futtta commented on May 25, 2024

it hooks into AO's autoptimize_filter_cache_getname (in autoptimizeCache.php) which is added in autoptimizeCache.php's getname() function which is called after CSS & JS minification as part of their cache() function.

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

@tdtgit I updated my code with your suggestion, thank you. But my problem is that the headers are not sent.
@futtta but on which level on the top-level action/filters is all that code happening?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

from autoptimize.

tdtgit avatar tdtgit commented on May 25, 2024

@grzegorz-janoszka Can you send me your site?
@futtta HTTP/2 Push is a cool feature and it's needed when HTTPS and HTTP/2 is became more popular. When will we see it as an option in AO plugin?

from autoptimize.

futtta avatar futtta commented on May 25, 2024

from autoptimize.

grzegorz-janoszka avatar grzegorz-janoszka commented on May 25, 2024

I just disabled all other plugins and stripped my own plugin to just those two AO filters and still nothing - no link headers sent by autoptimize_filter_cache_getname :(
Really have no idea where to look for it.
Yesterday I updated nginx on my site to support http2_push_preload and I would like to test it... and I can't :(

from autoptimize.

futtta avatar futtta commented on May 25, 2024

from autoptimize.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.