The plugin did its job
The page announces its own setup. This is the generator tag WP Rocket writes into the document:
<meta name="generator" content="WP Rocket 3.23.3.3"
data-wpr-features="wpr_delay_js wpr_defer_js wpr_minify_js wpr_async_css
wpr_preconnect_external_domains wpr_auto_preload_fonts wpr_oci wpr_image_dimensions
wpr_minify_css wpr_cdn wpr_preload_links wpr_desktop" />
Delay, defer, minify, async CSS: the full list a WordPress speed guide would hand you. And it works. WP Rocket’s delay feature keeps scripts from running until the visitor interacts with the page, and on this page it has rewritten fifteen script tags (nine files, six inline blocks) into a type the browser does not run:
<script type="text/rocketlazyloadscript" data-minify="1" id="et-core-common-js"
data-rocket-src="https://divi-tutorials.com/wp-content/cache/min/1/wp-content/themes/Divi/core/admin/js/common.js?ver=1786309545"
data-rocket-defer defer></script>
Seven external scripts are left to load normally: jQuery, jQuery Migrate, the cookie notice,
Divi’s own scripts.min.js (version 4.27.9), two files of a popup plugin and FitVids. In the
mobile run they come to 152 KB. For a page built in a visual builder with a membership
plugin, a Q&A plugin and a popup plugin on board, that is a lean first view.
And yet the same mobile run downloads 87 scripts, and the page scores 32.
One iframe, 70% of the requests
The mobile run, grouped by who serves each request:
| Served by | Requests | Bytes |
|---|---|---|
| The Twitch player (8 hosts) | 117 | 2.25 MB |
| divi-tutorials.com | 38 | 1.05 MB |
| www.gstatic.com (Cast SDK scripts) | 3 | 29.2 KB |
| maxcdn.bootstrapcdn.com (Font Awesome 4.5.0) | 1 | 6.9 KB |
| s.amazon-adsystem.com | 3 | 2.6 KB |
| data: and blob: URLs | 5 | — |
| Total | 167 | 3.34 MB |
Inside the Twitch share: 76 scripts (1.39 MB), a 446 KB WebAssembly video worker
(amazon-ivs-wasmworker…wasm) with its 53 KB loader, two web fonts, and 19 fetch calls. Of the
87 scripts in the run, 76 are Twitch’s, 7 are the site’s, 3 are the Cast SDK, and one is a blob.
Neither gstatic.com nor amazon-adsystem.com appears anywhere in the page’s own HTML.
All of it starts from one element in the hero, a Divi Code module:
<div class="et_pb_row et_pb_row_2 et_pb_row_fullwidth twitch_embed_container reverse-responsive et_pb_equal_columns">
…
<div class="et_pb_module et_pb_code et_pb_code_0">
<div class="et_pb_code_inner">
<iframe src="https://player.twitch.tv/?channel=nextab_de&parent=divi-tutorials.com"
frameborder="0" allowfullscreen="true" scrolling="no" height="100%" width="100%"></iframe>
</div>
</div>
This is why a well-configured caching plugin and a 32 can sit on the same page. WP Rocket’s
delay works by rewriting <script> tags in the HTML it serves. The iframe is not a script: it
is a second document, served by player.twitch.tv, and its 76 scripts are written into that
document. They never pass through WordPress, so no WordPress plugin can delay, defer or
minify them. The site did everything the plugin can do, and what the plugin can do ends at the
iframe’s border.
On a phone, the player is the second thing you see
The row holding the player has a class that matters more than it looks:
@media only screen and (max-width:980px) {
.et_pb_row.reverse-responsive { display:flex; flex-flow:column-reverse }
}
On desktop the row is two columns: the intro text on the left, the player on the right. Below
980 px the columns stack, and column-reverse flips their order, so the player, last in the
source, lands first: directly under the H1. That is a deliberate layout choice and it does what
it says. It also means the iframe is in the first screen on every phone, which takes the easy
fixes off the table. loading="lazy" defers what is far from the viewport; this is in it.
The phone gets the heavier picture
The page carries two PNGs with ordinary WordPress srcsets. Here is one:
<img decoding="async" width="862" height="801"
src="https://divi-tutorials.com/wp-content/uploads/2018/03/coding-isometric-01.png"
srcset="…/coding-isometric-01.png 862w,
…/coding-isometric-01-300x279.png 300w,
…/coding-isometric-01-768x714.png 768w"
sizes="(max-width: 862px) 100vw, 862px" />
On a narrow screen the browser picks the 768w candidate, as it should. Here is what each run actually downloaded:
| File | Width | PNG type | Bytes | Which run fetched it |
|---|---|---|---|---|
| coding-isometric-01.png | 862 | palette | 20,797 | desktop |
| coding-isometric-01-768x714.png | 768 | RGBA | 142,151 | mobile |
| email-inbox.png | 800 | RGBA | 20,516 | desktop |
| email-inbox-768x755.png | 768 | RGBA | 53,226 | mobile |
The smaller picture is the heavier file, 6.8 times heavier for the first one and 2.6 times for the second. For the first, the PNG header shows why: the original is an indexed-colour file, and the 768 px copy that WordPress generated from it was written back out as full RGBA. The phone pays 195 KB for two images the desktop gets for 41 KB. No desktop audit will ever show this, because the desktop never asks for those files.
The second image has another story. It is the only image on the page marked
fetchpriority="high", and it sits inside a newsletter overlay printed before the header:
<div id="divi-overlay-container-214" class="divioverlay-container overlay-container" aria-hidden="true">
…
<img fetchpriority="high" decoding="async" width="800" height="786"
src="https://divi-tutorials.com/wp-content/uploads/2018/03/email-inbox.png" … />
The overlay waits for an exit-intent trigger. Until then it is hidden, and the highest-priority image request on the homepage is for a popup nobody has opened.
What we changed, and what we did not
| Metric (mobile) | Original | Through WebSpeed |
|---|---|---|
| PageSpeed score | 32 | 64 |
| First Contentful Paint | 11.3 s | 4.1 s |
| Speed Index | 11.3 s | 5.9 s |
| Largest Contentful Paint | 15.4 s | 7.0 s |
| Time to Interactive | 18.3 s | 7.0 s |
| Total Blocking Time | 1,122 ms | 51 ms |
The mobile LCP element is the same in both runs: the H1 text. Our run made 131 fewer requests than the original, and 117 of those are Twitch’s. The same seven first-party scripts load in both runs, each within 16 bytes of its original size; what is missing from ours is the player. Not every millisecond of the blocking time was Twitch’s, but by volume the player is nearly everything that left the test window.
Holding the iframe back is most of what happened here. The rest: images below the first screen load lazily (image bytes 448 KB → 181 KB), eight small stylesheets (17 KB together) no longer load as separate files, and the 768w PNG that does still load arrives as a 20,043-byte WebP instead of 53,226 bytes of PNG. Some of what we did went the wrong way, and the limits below list it, the desktop LCP first.
Why this is not a Divi problem
On the first view, Divi itself is a 98 KB script and a 78 KB stylesheet. The site’s own HTML arrives brotli-compressed, the plugin stack is configured with care, and the reverse-on-mobile row is a builder feature working exactly as designed. Nothing on this page is slow because it was made in Divi.
What a builder cannot do is tell you that the Code module you pasted an embed into will pull in 117 requests from eight hosts you do not control, or that the 768 px thumbnail it generated is heavier than the file you uploaded. Both are invisible from the editor, and both are invisible to a caching plugin, which sees the HTML and only the HTML.
Fix it yourself
None of this needs us. It needs someone to do it, test that nothing broke, and do it again after the next template change — which is the part we sell.
- Put a facade where the player is Replace the iframe with a still image of the player and a play button, and insert the real iframe on click. The first screen looks the same, and the 117 requests start only for the visitor who wants the stream. Native lazy loading will not do this for you here: on phones the player sits directly under the headline, which is exactly where loading="lazy" stops deferring anything.
- Do not hide it on phones with CSS Turning the module off for small screens with a display:none rule changes what the visitor sees, not what the browser downloads: a hidden iframe still loads its document. If the player should not exist on mobile, it has to be absent from the markup.
- Compare the bytes of every srcset candidate The 768w copies of two images on this page are heavier than the full-size files they were cut from, and phones are the ones that pick 768w. Either run the generated sizes through the same optimizer as the originals, or drop the candidate. A smaller candidate that weighs more than a larger one is always a bug, and it is invisible on a desktop.
- Take the priority off the popup image The only image carrying fetchpriority="high" on this page belongs to a newsletter overlay that is marked aria-hidden="true" and waits for an exit-intent trigger. Remove the attribute, or lazy-load images inside overlays: nothing in a closed popup should be first in line.
What we measured, and what got worse
PageSpeed Insights (Lighthouse, mobile and desktop) run against the live site
and against the same page served through the WebSpeed proxy, on
29 Sept 2026. Audit 348cd772d3f82d — the numbers
on this page are frozen at that run and are not restated later.
Proxy response re-checked on 5 Oct 2026: the proxied URL returns the page itself, not a redirect back to the origin. That check exists because a redirect makes both halves of a before/after the same page, and the improvement pure noise.
What the page still loaded
The accelerated run made 36 requests against the original's 167, and transferred 1 MB against 3.3 MB. We print this because a score can rise for two very different reasons — the page got faster, or the page got less of itself — and across our audit base the second is common enough to be worth ruling out in the open.
What got worse or stayed the same
- Most of the mobile gain is the Twitch player not loading during the test. Our run made zero requests to Twitch hosts: the slot keeps its size, but the player starts only after the visitor interacts with the page or some time after load, and within the measured window it never started. That is the same trade a click-to-load facade makes, minus the still image in the box. If the live stream is the point of the hero, a visitor sees it later through us than on the original, and 36 requests against 167 is that trade written as a number.
- Desktop got slower at the top. Largest Contentful Paint went from 0.96 s to 1.35 s and First Contentful Paint from 0.60 s to 0.71 s. The desktop LCP element is the hero's parallax background, and we held it back: the delay before it started loading grew from 51 ms to 632 ms. The desktop score still rises (70 → 95) because blocking time falls from 635 ms to 17 ms, but the first thing a desktop visitor sees arrives later.
- We added bytes the original never downloaded. Two Hack font files (210 KB together) are preloaded at the very top of our run, which puts fonts at 452 KB against the 241 KB of the site's own fonts the original loaded. One background texture is requested three times: a 317-byte HTML answer from academy.nextab.de, a WebP copy and the original JPEG, where the original page fetched it once.
- The mobile result is better, not good. LCP is still 7.0 s and the score is 64. Lighthouse estimates 1.45 s of render-blocking savings left on our run against 0.15 s on the original; that is an estimate, not a measurement, but it says the critical path we build here is not clean.
- The origin serves two versions of this page, one per device, each with its own critical CSS from WP Rocket (23,942 against 27,918 characters, compared on 5 October 2026). Both of our runs received the same document, and the copy we fetched is the desktop one. The two versions have the same scripts, stylesheets, images and iframe, so this does not change what loads, but our mobile run did not use the critical CSS WP Rocket generated for phones.
- One page, one day. The original mobile score came out at 38, 32 and 32 over three runs, and the median is what is printed. This is a teardown, not a study: nothing here generalises to Divi sites as a class.