Request Waterfall Issues
What This Means
A request waterfall occurs when resources are loaded sequentially rather than in parallel. This causes:
How to Diagnose
- DevTools > Network tab
- Reload page
- Analyze the waterfall chart
- Look for:
- Long chains of dependent requests
- Resources waiting for others to complete
- Large gaps between requests
Key Metrics to Check
| Pattern |
Problem |
| Staircase pattern |
Sequential loading |
| Large gaps |
Blocking resources |
| Late critical resources |
Priority issues |
| Third-party chains |
External dependencies |
WebPageTest Analysis
- Run test at webpagetest.org
- View waterfall chart
- Check "Connection View" for parallel request limits
General Fixes
Preconnect to Critical Origins
<!-- Establish early connections -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<link rel="dns-prefetch" href="https://analytics.example.com">
Preload Critical Resources
<!-- Preload LCP image -->
<link rel="preload" as="image" href="/hero.webp">
<!-- Preload critical font -->
<link rel="preload" as="font" href="/fonts/main.woff2"
type="font/woff2" crossorigin>
<!-- Preload critical CSS -->
<link rel="preload" as="style" href="/critical.css">
Inline Critical CSS
<!-- Inline above-the-fold CSS -->
<style>
/* Critical CSS inlined here */
body { margin: 0; font-family: system-ui; }
.hero { height: 100vh; display: flex; }
</style>
<!-- Load rest asynchronously -->
<link rel="preload" as="style" href="/main.css"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/main.css"></noscript>
Eliminate Render-Blocking Scripts
<!-- Bad: Blocking script -->
<script src="/app.js"></script>
<!-- Good: Async for independent scripts -->
<script async src="/analytics.js"></script>
<!-- Good: Defer for dependent scripts -->
<script defer src="/app.js"></script>
<!-- Good: Module scripts (deferred by default) -->
<script type="module" src="/app.js"></script>
Optimize Third-Party Loading
// Delay non-critical third-parties
function loadThirdParties() {
// Load after user interaction or delay
const scripts = [
'https://analytics.example.com/script.js',
'https://chat.example.com/widget.js'
];
scripts.forEach(src => {
const script = document.createElement('script');
script.src = src;
script.async = true;
document.body.appendChild(script);
});
}
// Trigger on interaction
['mousedown', 'touchstart', 'scroll'].forEach(event => {
window.addEventListener(event, loadThirdParties, { once: true });
});
// Fallback after delay
setTimeout(loadThirdParties, 5000);
Bundle Optimization
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// Separate vendor bundle
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
},
// Common chunks
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
}
}
}
}
};
HTTP/2 Optimization
# Enable HTTP/2 server push (use sparingly)
location / {
http2_push /styles/critical.css;
http2_push /scripts/app.js;
}
# Or use Link headers
add_header Link "</styles/critical.css>; rel=preload; as=style";
Image Loading Priority
<!-- High priority: LCP image -->
<img src="/hero.webp" fetchpriority="high" alt="Hero">
<!-- Low priority: Below-fold images -->
<img src="/footer-bg.webp" fetchpriority="low" loading="lazy" alt="">
Avoid CSS @import Chains
/* Bad: Creates waterfall */
@import url('fonts.css');
@import url('layout.css');
@import url('components.css');
/* Good: Combine or use link tags */
<!-- Better: Parallel loading -->
<link rel="stylesheet" href="fonts.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="components.css">
JavaScript Dynamic Import
// Split code to avoid loading everything upfront
// Only load when needed
async function loadChartLibrary() {
const { Chart } = await import('./chart-library.js');
return new Chart(document.getElementById('chart'));
}
// Load on demand
document.getElementById('show-chart').onclick = async () => {
const chart = await loadChartLibrary();
chart.render(data);
};
Verification
- DevTools waterfall shows parallel loading
- Critical resources load early
- No long dependency chains
- LCP occurs within 2.5s
Further Reading