Skip to content
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

ResourceHints Link header in HTTP 200 #30758

Open
4 tasks done
IO-Fire opened this issue Jan 25, 2025 · 12 comments
Open
4 tasks done

ResourceHints Link header in HTTP 200 #30758

IO-Fire opened this issue Jan 25, 2025 · 12 comments

Comments

@IO-Fire
Copy link

IO-Fire commented Jan 25, 2025

Describe the feature

Adds a Link header from the renderer to give the browser hints in the HTTP 200 response of additional resources while downloading / parsing the server response payload. The intention is to decrease page load time by allowing the browser to simultaneously request upcoming resources needed by the DOM before they are identified.

If the browser or any proxies don't support HTTP 103 Early Hints, then including hints in the later HTTP 200 still provides early notice. Implementing the feature should hopefully be of minimal operating disruption compared to adopting HTTP 103 while providing some of the benefits.

Anecdotal evidence suggests that unloaded CSS files are blocking the client side loading of pages until fetched as a subsequent resource in Lighthouse testing. Hints may reduce page load time by reducing the gap between the resource identified and render awaiting resource including JS and CSS.

Feedback and suggestions are welcome!


Example of how it could initially work:

main...IO-Fire:nuxt:feature/write-link-header
Although this implementation appears to only show JS and not styles (test sample size of 1). This may also impact the current 103 early hints feature.

SSG Support

Update the route rules' headers for each page before the creating completing the route rendering.
OR
May require changes in the Nitro static presets to encompass the headers being identified at render time rather than at route creation.

Header rule limits

Some hosting providers including Cloudflare Pages have limits on headers.

Cloudflare Pages limits the total number of "header rules" similar to route rules.

100 pages / routes with headers would use 100 headers rules. This may limit the number of pages within a site without workarounds such as Baroshem/nuxt-security#504 (comment)

Module Implementation

Module implementation may not be possible as access to the render's data may not be exposed to a module.

Additional information

  • Would you be willing to help implement this feature?
  • Could this feature be implemented as a module?

Final checks

@IO-Fire
Copy link
Author

IO-Fire commented Jan 26, 2025

This could also help generate HTTP 103 responses on Cloudflare Pages with Early Hints with the headers integration.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 27, 2025

Working on a module version in the meantime. It is likely to be less performant than a baked in feature but should have no user performance impact when using SSG.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 27, 2025

Page load performance increase could be significant if the page contains large style elements in the head before links to additional resources. Without the resource hints, the browser would have to download and read past the inline styles before discovering additional resources to fetch, missing the opportunity to download and read resources simultaneously. While this example assumes the server and browser support content streaming, a saving could still be made before the browser parses the document.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 27, 2025

Will implement as module for the SSG support. Although the simple change for Link headers for SSR in the first issue comment could provide some value in the meantime (possibly JS only). Both can be achieved with a module, SSG is much tricker as the build to route rules logic is more complex.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 30, 2025

Good news, here is an implementation of the module to test:
https://github.com/IO-Fire/nuxt-resource-hints

@IO-Fire

This comment has been minimized.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 30, 2025

Added important edit to last comment.

Having now tested without network throttling and more powerful hardware the results are minimal potentially may interrupt the browser scheduling by using prefetch instead of preload, as discussed below.

There may be still an argument for poorer network connectivity like the example image show, such as HTTP 103 early hints, or where the browser has enough time to schedule the CSS before the parser identifies the resources. Perhaps the blocking parameter (not available in Firefox and Safari) may schedule the resource at the correct highest priority instead of just high.

Note that the browser (Chromium based) would throw console warnings for preload for CSS as it does not count as being used by the window.

Note while it appears multiple network requests are made for the same resource, this appears to only be present on the client side and doesn't show in the logs of HTTP server used.

Firefox appears to ignores the prefetch even when the connection is slow, perhaps may consider preload.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 30, 2025

Updated the module again. Performance uplift is available in Chromium based browsers consistently in a test project of at least 10ms to 30ms Largest Contentful Paint (LCP) saving. This is possible without network throttling or CPU throttling, the increase applies to slower hardware / connections too.

Tests

Using a local project, test with headers from the module include and excluded.

  • Using an empty browser session with no cache, load the page 2 times.
  • Clear the browser session and cache.
  • Measure results, repeat for each combination.

LCP without module:

LCP 0.88s

LCP 0.88s, Render blocking resources CSS

3G Network throttling
LCP 5.52s

LCP with module:

LCP 0.84s

30ms time saving

OR

LCP 0.87

10ms time saving

No render blocking resources

3G Network throttling
LCP 5.01

510ms time saving

Conclusion

While a 10ms / 30ms time saving could be within margin of error, the results are consistent and scale as the network filtering is applied. The Network filtering helps to reduce the reliance on the web server speed for returning resources as the requests are slowed to allow the web server more time to deliver requests.

It is recommended that you conduct your own tests before using this module in production.

@IO-Fire
Copy link
Author

IO-Fire commented Jan 31, 2025

@coderabbitai summerise

Copy link

coderabbitai bot commented Jan 31, 2025

Tip

For best results, initiate chat on the files or code changes.

@IO-Fire Here's a summary of the discussion:

The proposal started as a core Nuxt feature to add resource hints via Link headers in HTTP 200 responses, potentially working with Cloudflare Pages' Early Hints (HTTP 103). It was subsequently implemented as a module (nuxt-resource-hints) to support both SSR and SSG.

Performance testing revealed:

  • Consistent 10-30ms improvement in Largest Contentful Paint (LCP) on Chromium browsers
  • More significant improvements (510ms) under 3G network conditions
  • Variable browser support (Firefox ignores prefetch)
  • Most effective for scenarios with slower network connectivity

The module provides a practical solution for improving page load performance, particularly beneficial for users on slower connections, though gains are modest on fast networks.

(`・ω・´)ゞ

@IO-Fire
Copy link
Author

IO-Fire commented Feb 3, 2025

This could also help generate HTTP 103 responses on Cloudflare Pages with Early Hints with the headers integration.

Success! Cloudflare is generating 103 Early hints from the link header:

Network request with added Early Hints section

This also works for pages proxied to custom domains.

@IO-Fire
Copy link
Author

IO-Fire commented Feb 3, 2025

Considering the simplicity of the nuxt-resource-hints module, it may be possible to create a Nitro module to benefit other projects using Nitro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants