-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: long (~500ms TTFB) initial load time for images? #12047
Comments
This is strange... I can replicate the behaviour you're seeing. Using Chrome's Network tab with caching disabled and javascript enabled I see similar results to yours. However, if I try and dig into this more using the Performance tab on Chrome, I see the normal 'fast' behaviour. I'm reading this as:
which is roughly consistent with your ~100ms no javascript load time.
I'd need to check this, but I think the image request shouldn't happen until after the page has been hydrated. Before then, there should only be the svg placeholder (and the In short - I'm not sure what's going on here! I'd be interested to hear what anyone else thinks. |
Yes, the full image won't be available until the AFAIK, TTFB in this case should only be affecting the request/response side, and thus shouldn't be different to noscript, once cached, then you get the fast TTFB.
You can check the TTFB and what's causing the bulk of that time with this tool Another consideration could be caching for offline or via service-worker(I guess that's offline?), Gatsby blog can have cache disabled, but will still pull the images from a service-worker cache in the network. (Not the case with given link in this case though). I tried the linked example, and can also replicate same results both of you have mentioned. Slightly worse with my connection/location, sometimes TTFB could be as large as 1-2s, but I would get the 500ms and with JS disabled <100ms. The difference between browser refresh or performance profiler tab refresh for the TTFB network tab results is really odd!(I wonder what causes that!) Now to make it even more confusing :) Here's one of my own netlify projects for some testing. Rather than a single image, I have 24(so it varies based on viewport what will get loaded when JS is enabled). You'll notice that with JS disabled it's worse for TTFB. I assume it's due to all the requests being made at once, even incurs queues for me. Perhaps this is a factor? Running a build locally via nginx docker container, there isn't any noticeable difference(<1ms TTFBs), even at "Slow 3G" network speed and "6x throttle" for CPU which yielded 2s TTFBs for both. Performance monitor also showed no difference here. Might be something specific to Netlify env or network that differs from my local env. My local version isn't entirely the same at the moment, so I'd need to push it to Netlify to confirm for sure. |
Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open! Thanks for being a part of the Gatsby community! 💪💜 |
I've encountered the same exact issue except I'm not on Netlify. Our CDN is generally very fast when serving images directly but as soon as they are on a page with Gatsby the images take 10x longer sometimes. I've come to the conclusion it has something to do with Gatsby's preloading or javascript files. Here is a test with 3 core javascript files blacklisted and on a slow 3G connection speed. http://webpagetest.org/result/190503_PG_524a4214f2fb4822da42ff7aa6614c73/3/details/#waterfall_view_step1 Here is the same test without any files blacklisted I'm not sure what about these JS files is the issue but they are making our site's TTFB performance take WAY longer than it should. I've been looking for a way to disable preloading in Gatsby but I haven't been successful finding anything. I found out how to disable pre-fetching but they are not the same thing. I hope these tests help someone find the root cause. |
Summary response (TL;DR)It appears the issue is due to HTTP/2, not Gatsby. All requests are sent at the same time for that HTTP/2 socket to handle(transfer responses back). The responses are queued onto a stream back to the browser client, and TTFB is reported as those assets arrive. Thus they're including the download time of all requests served prior to them(from the same HTTP/2 stream) A large portion of that which makes up the notable difference is about 200KB of JS(3 requests). If you want to deliver the images ahead of that, they'll need to be served from a different HTTP/2 socket. As this dependency visualization shows, serving them from a different (sub-)domain should be sufficient. Although as I point out at the end of my post, domain sharding probably has no benefit to your sites performance here, it'll likely just make the TTFB look the way you want, with no meaningful gains due to overhead of opening a new connection. Original response
TTFB is the time from the request to when the server responds with the first byte to the browser. So that's not really anything to do with Gatsby, but rather the latency over the network and any latency on the server end. Something that might be playing into it is the amount/rate of requests at once. Looking at the headers HTTP/2 is used, performance for multiple requests at once with this improves over HTTP/1, but many tests have shown it to degrade as the request count increases, so this may be impacting the outcome as well. There are 6 additional requests for We can also see that overall load time is only about 1 second longer(~10%+), which is fair considering the increase of requests and additional weight they add. What is noticeably different is about 4 seconds difference to visually complete(~50%+). Inspecting request headers for the hidpi logo png image: 1st test link:
2nd test link:
The image is being requested at a later point but also has a higher priority assigned to it. This article details an aspect of HTTP/2 regarding priorities:
So that's interesting. Looking at the 2nd test results request data at the bottom, the problem file you cited, along with a few others are requested earlier than the images, but are all fired off at roughly the same time along with the images. The priority values for all are MEDIUM, rather than associating LOW to the images like in the 1st test link. What is probably happening here is, all requests are sent off at the same time. The server now queues all those requests up, and once that's ready, begins to stream them over the single HTTP/2 socket to the browser client. Due to the way HTTP/2 works, the assets with higher priority in the queue will be delivered first, and then afaik it'll depend upon dependencies of those requests for ordering. Requests 2-4 on the 2nd test link take roughly ~1,100 ms to download, you can actually see the TTFB column correlates with the content download column. This is why the TTFB is appearing so high. That first byte is delayed until that request is responded to from that single socket streaming content to the client. If you use HTTP/1 then as long as the additional overhead of new requests is not too high, you'd see much lower TTFB, but that might not actually mean much of a difference. As you continue to move down comparing the columns, you'll notice TTFB stops increasing instead of jumping another 700ms, why is that? Looking at the request header data for request 12:
The prior request for a large 60KB image was:
It wasn't dependent on the previous request like others have been, instead it depended on stream 19 which was request 10, star.png.
So I got that slightly mixed up, the dependency chain affects placement on the queue, with priority adjusting when that request is served respective to it's adjacent dependencies. The hidpi logo image had medium priority, with the other large image(request 11) also being medium, but all those between the two were lower priority. Turns out webpagetest has a handy link under one of the charts to visualize the dependency list better! That better illustrates it :) That shows the hidpi logo having larger mobile jpg dependent on it with a higher priority value than the chain of lower priority dependencies next to it. I'd have thought it would download before the lower priority chain, but for whatever reason, it didn't, nor was the lower priority dependency chain respected. Perhaps as the article stated:
But that should clear up this issue. What you've witnessed is nothing to do with Gatsby, it's just the way HTTP/2 works, the TTFB isn't accurate here per request since they're all served over a single stream but all requested/queued up front. That extra 4 seconds until "Visually Complete" is a valid a concern but appears to be due to the extra requests to other platforms, including Google Maps and the GraphQL staging-api subdomain(adds additional lookups, you can use this to reduce that TTFB via domain sharding), other than the initial JS payload difference, which seems to reflect accurately on time to interactive(more meaningful metric) offset difference. Google Developer docs on HTTP/2 Stream Prioritization
Cloudflare demonstrates that TTFB is not an accurate/reliable metric:
KeyCDN on Domain Sharding
It's generally ill-advised to utilize domain sharding these days with HTTP/2, but may be required if you want another connection/socket open for the assets TTFB that you want to shorten due to their delay in the stream queue. Note this will incur some overhead to open up a new socket(DNS lookup, TLS handshake, etc), which from your test results looks to be around 1+ seconds to perform anyhow, so there may not be any gains there other than the TTFB looking shorter in the network graph, but not actually having arrived any faster. |
Yes. The total content to download when JS is not blocked is almost double the size in the referenced site you provided. HTTP/2 streams all requests from the same hostname over a single socket instead of multiple requests like HTTP/1. The long TTFB is due to all requests being sent upfront at once, and reported once that particular asset has received it's first byte by the browser. They are in fact blocked by assets earlier in the queue of the stream.
Using the You would otherwise need the server to prioritize the assets ahead of the JS payload in the stream. Preloading assets above the fold might work, or using Server Push(you'll lose any browser cache benefit) should place priority above anything else. You can also try domain sharding, but the initial overhead probably removes any benefit(it will just render/report a shorter TTFB). It's not really worthwhile in majority of cases with HTTP/2 these days. Going to close this issue as it's resolved. |
Thanks for looking into this. I am actually using the I know you said this is not a Gatsby issue, but I have to disagree. I hope I'm not too harsh, but I have to say, If I compare these two options for creating a straightforward static website:
...choosing Gatsby means literally 6x load times for critical images. I know you said it's a HTTP2/resource prioritization issue, but a simple website just shouldn't be bundled with so much JavaScript that loading critical assets is delayed by 500ms. One of the main selling points of Gatsby is that it's supposed to improve your web site performance for you. Here it's doing literally the opposite of that. |
Thank you for the detailed response back, I really appreciate that. What you said about HTTP2 making TTFB appearing higher because of other higher priority things loading makes a lot of sense. I was able to disable preloading on our site and it didn't have a noticeable impact on overall load time of the page. Even though the waterfall started looking better for us with faster TTFB images, the overall load time did not improve, which makes a lot of sense given your explanation. For those curious here is the code we used to disable preloading on our site, this is not something we plan on keeping since it didn't improve the metrics we were looking to improve (mainly load time.) In gatsby-ssr.js
@baobabKoodaa This article might also help you https://developers.google.com/web/updates/2019/02/priority-hints Also Gatsby is a little more than an static site generator, it's powered by React when the page hydrates and acts as an SPA when clicking through gatsby linked pages. This let's us developers write in the frontend framework we love but still have the benefit of a static site. Like everything in life there is tradeoffs to our decisions, one of the tradeoffs we make when using Gatsby is that will have a little more JS than other options because we chose to use React to write the website with. |
I wonder if there's a way to ship the non-critical JS assets after shipping the critical images? |
@baobabKoodaa Here's the HTTP/2 dependency graph and Request Details for your site via webpagetest. You can see priority wise your fonts are quite high, as is the JS, and these priorities are defined by the browser afaik. Using priority hints as @itmayziii helpfully linked to should be a way to improve prioritizing the loading of your image resource,
Your comparison isn't apples to apples though. Gatsby provides static upfront but then also provides JS to hydrate a React app for additional benefits to the web experience. The Something you could try is editing the If switching to prefetching works for your needs, I'd suggest raising a new issue about it, so that the core devs can consider supporting it via gatsby-config.js to handle it for you as opt-in. It could potentially even query the Web API for saveData which some browsers(particularly mobile devices) can enable so that the JS could have lower priority this way on slow connections perhaps? Based on the Request Details link, using domain sharding in your case may get your images to arrive sooner. Other than all that above, I don't see how Gatsby can help you with this. The priority hints are fairly new and afaik only supported in Chrome? You can lower priority of the JS preloads like that link shows instead of increasing priority for specific images if you like. Ultimately though, this is the purpose of having placeholders in the first place. |
Hey, thanks again. I really appreciate the help. I tried to find the |
@baobabKoodaa that's odd, I have plenty of preload tags in the head of my index.html, but only if I do Which were you using? |
Oops! Ok so here is 2 versions of my site, one direct output of https://test-preload.netlify.com At least on latest Chrome on Ubuntu the behavior is same for both: the main image loading is delayed by >500ms because of JS. I don't understand why this happens. Isn't the browser supposed to fetch the |
Typically yes. Did you inspect the markup? I see the images are only defined via noscript tags. That suggests no image was added with the |
I am adding the top image as critical:
I confirmed with a console log that it works as intended (yields true for the top image, false for other images). I don't know why gatsby-image still puts it inside I guess what I should try next is maybe replace |
I think the right solution here is to use add |
That alone wouldn't raise it's priority over the JS assets being preloaded for hydration into a react app though? JS has priority over images last I knew? |
It's complicated :-D https://blog.cloudflare.com/better-http-2-prioritization-for-a-faster-web/ Adding a preload would at least make the critical image equal priority with JS. |
Yeah I just came across that new article, will read through it and a few others I've got up on the topic before putting together my input on the related issue you opened up. I am not sure where I saw the information prior, but there was a nice table(at least for Chrome) that showed the prioritization rules for resources by type. Which iirc, while different asset types had their own priority values/levels, this order was still used when deciding order for assets given equal priority? Priority hints should assist in future if they become more widely adopted. Otherwise domain sharding may sometimes work, or HTTP/2 server push outside of Gatsby for enforcing priority. |
Summary
My Gatsby site is hosted on Netlify. If I hard refresh a single image, Netlify CDN delivers it to me in about ~50ms. If I hard refresh a very light page with that same image, it takes ~500ms to deliver the image. What's causing this?
Most of the time is spent in "Waiting (TTFB)".
If I disable JavaScript, the image loads in ~100ms.
So, my guess is that the request for the image is sent, then the page is "hydrated" into React, and it's blocking the browser from receiving content. Is this correct? Can I do something to improve the image load time?
I was testing with this page.
The text was updated successfully, but these errors were encountered: