LiteSpeed Guest Optimization vs Splash Screens — Fixing FOUC
· 6 min read
A client's WordPress site had a branded splash screen — a full-viewport overlay with their logo and a brief loading animation that displays while the page renders underneath. It looked polished and worked perfectly. Then I enabled LiteSpeed Cache's Guest Optimization feature and the splash screen broke in a way that was worse than having no splash screen at all.
Visitors were seeing a flash of the full page content — unstyled or partially styled — for about half a second before the splash screen appeared on top. The classic Flash of Unstyled Content problem, but made worse because the splash screen was supposed to be hiding exactly this kind of rendering jank.
How Guest Optimization Changes CSS Delivery
LiteSpeed Cache's Guest Optimization is designed to serve a highly optimised, cached page to first-time visitors who don't have any cookies set. It's effective — pages load fast and Core Web Vitals scores improve significantly.
Part of that optimisation involves changing how CSS is delivered. Guest Optimization combines CSS files, loads them asynchronously with rel="preload", and injects them via JavaScript once they're downloaded. This is great for overall performance metrics because it prevents render-blocking CSS from delaying the Largest Contentful Paint.
The problem is that the splash screen depends on its CSS being available immediately, before any page content renders. The splash overlay needs to be styled and visible from the very first paint. When Guest Optimization defers that CSS, the browser renders the page content first (because no CSS is blocking it), and then the splash screen CSS loads and the overlay appears on top — but by then the visitor has already seen the unstyled content flash behind it.
Diagnosing the Sequence
I confirmed the issue by watching the network waterfall in Chrome DevTools with cache disabled. Without Guest Optimization, the splash screen CSS loaded as a render-blocking stylesheet in the <head>, the browser painted the splash overlay first, and the page content rendered invisibly behind it. Correct behaviour.
With Guest Optimization enabled, the CSS was moved to an async preload. The browser painted the page with no styles applied, then the combined CSS file loaded, and then the splash overlay appeared. The gap between first paint and splash appearance was around 300-500ms — short, but very noticeable.
The key indicator in the page source was the CSS link tag changing from a standard rel="stylesheet" to rel="preload" as="style" with an onload handler that swapped it to a stylesheet after download. This is a well-known performance pattern, but it's incompatible with any CSS that needs to be present at first paint.
The Fix: Excluding Splash CSS from Optimization
LiteSpeed Cache provides granular control over which CSS files are included in its optimisation pipeline. The fix was to exclude the splash screen stylesheet from Guest Optimization's CSS handling so it loads synchronously while everything else continues to load asynchronously.
In LiteSpeed Cache > Page Optimization > CSS Settings, I added the splash screen CSS file path to the CSS Excludes list. The splash screen used a dedicated stylesheet — splash-screen.css — so the exclude was clean:
splash-screen.css
For sites where the splash CSS is part of a larger theme stylesheet rather than a separate file, the approach is different. In that case, I extract the splash-specific CSS (typically 20-30 lines covering the overlay positioning, background colour, logo sizing, and animation) and inline it directly in the <head> using LiteSpeed's Inline CSS option or a small code snippet in the theme's functions.php:
add_action( 'wp_head', function () {
echo '<style id="splash-critical">';
echo '.splash-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; ';
echo 'z-index: 99999; background: #ffffff; display: flex; align-items: center; ';
echo 'justify-content: center; transition: opacity 0.3s ease; }';
echo '</style>';
}, 1 ); // Priority 1 = load before almost everything else
Inlined CSS is never deferred — it's part of the HTML document itself — so the splash overlay is styled from the very first render regardless of what Guest Optimization does with the external stylesheets.
Verifying the Fix
After excluding the splash CSS, I cleared the LiteSpeed cache entirely and tested in an incognito window to simulate a first-time guest visit. The rendering sequence was correct again: splash overlay visible immediately, page content loading invisibly behind it, splash fading out once the page was ready.
I also verified that Guest Optimization was still working correctly for everything else. The combined CSS file was still being preloaded, non-critical styles were still deferred, and the Core Web Vitals scores remained strong. The only difference was one small CSS file loading synchronously — negligible impact on performance metrics.
Settings That Matter
If you're running into similar FOUC issues with LiteSpeed Guest Optimization, these are the settings to check:
- CSS Combine — if enabled, check whether critical-path CSS is being bundled into the combined file and therefore deferred
- CSS Excludes — add any CSS files that must load synchronously (splash screens, above-the-fold critical styles, font-face declarations that prevent layout shift)
- Load CSS Asynchronously — this is the core setting that changes
rel="stylesheet"torel="preload". Excluding specific files from this is the surgical fix - Guest Optimization — if you only see the issue for first-time visitors (not logged-in users or returning visitors with cookies), Guest Optimization's separate CSS handling is the cause
The broader takeaway: performance optimisation plugins make assumptions about which CSS can be deferred safely. Most CSS can be. But anything that controls above-the-fold rendering — splash screens, critical layout styles, font loading — needs to stay synchronous. The performance hit of loading one small stylesheet synchronously is negligible compared to the user experience hit of a FOUC.
This is the kind of subtle issue I catch during routine performance audits as part of my WordPress maintenance service.
Need help with something similar? Check out my maintenance plans.
