This comprehensive PageSpeed optimization suite consists of four interconnected documents designed to support developers and DevOps teams in achieving optimal web performance across mobile and desktop platforms.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MASTER INDEX (This Document) β
β Navigation & Overview of All Resources β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββΌββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β CHECKLIST β β IMPLEMENTATIONβ β QUICK REF β
β Complete β β Guide β β Cheat Sheet β
β Coverage β β Tactical β β Rapid β
β Reference β β Code & How β β Access β
βββββββββββββββ ββββββββββββββββ ββββββββββββββββ
Master Index (You are here)
βββ Quick Start Guide
βββ How to Use This Suite
βββ Learning Paths
βββ Workflow Integration
βββ Metrics & Monitoring
βββ Tools Reference
βββ Success Criteria
βββ Common Scenarios
βββ Troubleshooting
βββ Additional Resources
PageSpeed Universal Optimization Checklist
βββ Core Web Vitals (11 sub-sections)
βββ Performance (6 major sections, 20+ sub-sections)
βββ Accessibility (4 sections)
βββ Best Practices (4 sections)
βββ SEO (5 sections)
βββ Testing & Monitoring (4 sections)
βββ Mobile Optimization
βββ Desktop Optimization
βββ Progressive Enhancement
βββ Visual Optimization
βββ Analytics & Measurement
Implementation Guide
βββ Quick Start (Priority Matrix)
βββ Compression Setup
βββ Image Optimization (with tools)
βββ Lazy Loading (code examples)
βββ Critical CSS (extraction methods)
βββ JavaScript Optimization
βββ Font Optimization
βββ Caching Configuration
βββ Resource Hints
βββ Third-Party Optimization
βββ Web Vitals Monitoring
βββ Build Process
βββ Continuous Monitoring Setup
Quick Reference Cheat Sheet
βββ Core Web Vitals Targets
βββ Top 10 Quick Wins
βββ Image Commands
βββ Code Templates
βββ Cache Headers
βββ Testing Commands
βββ Optimization Checklists
βββ Common Issues & Fixes
βββ Performance Budgets
βββ SEO Quick Checks
βββ Accessibility Quick Checks
βββ Essential Tools
File: PageSpeed_Universal_Optimization_Checklist.md
Purpose: Comprehensive, atomic, MECE checklist covering all aspects of PageSpeed optimization
Best Used For:
- Complete audit of website performance
- Planning optimization initiatives
- Tracking progress across categories
- Ensuring nothing is missed
- Team alignment on standards
Structure:
- 11 major categories with 100+ checklist items
- Performance threshold references
- Success criteria definitions
- Resource links and documentation
- Implementation roadmap (4-phase approach)
Key Sections:
- Core Web Vitals Optimization
- Performance Optimization
- Accessibility Optimization
- Best Practices Optimization
- SEO Optimization
- Testing & Monitoring
- Mobile-Specific Optimization
- Desktop-Specific Optimization
- Progressive Enhancement
- Visual Optimization
- Analytics & Measurement
File: PageSpeed_Implementation_Guide.md
Purpose: Step-by-step tactical instructions with code examples
Best Used For:
- Actually implementing optimizations
- Copy-paste code snippets
- Configuration templates
- Build process setup
- Understanding how to execute each optimization
Structure:
- 12 implementation sections
- Real code examples for every optimization
- Configuration files (Nginx, Apache, Webpack, Next.js)
- Priority matrix for quick wins
- Automation scripts and tools
Key Sections:
- Compression Setup
- Image Optimization
- Lazy Loading Implementation
- Critical CSS Extraction
- JavaScript Optimization
- Font Optimization
- Caching Configuration
- Resource Hints
- Third-Party Optimization
- Web Vitals Monitoring
- Build Process Optimization
- Continuous Monitoring
File: PageSpeed_Quick_Reference_Cheat_Sheet.md
Purpose: Rapid-access reference for most common optimizations
Best Used For:
- Quick lookups during development
- Emergency fixes
- Onboarding new team members
- Daily development reference
- Print and keep at desk
Structure:
- Top 10 quick wins
- Essential code snippets
- Command line references
- Common issue troubleshooting
- Testing commands
- Deployment checklist
Key Sections:
- Core Web Vitals targets
- Top 10 quick wins
- Image optimization commands
- Critical templates (responsive images, fonts, caching)
- Common issues & fixes
- Performance budgets
- Essential tools list
Phase 1: Planning
- Review the Checklist to understand full scope
- Review the Implementation Guide priority matrix
- Establish performance budgets from Cheat Sheet
- Set up baseline metrics
Phase 2: Foundation
- Use Cheat Sheet "Top 10 Quick Wins"
- Follow Implementation Guide Week 1 checklist
- Track progress in Checklist document
- Verify with testing tools
Phase 3: Core Optimizations
- Use Implementation Guide sections 1-9
- Reference Checklist for completeness
- Use Cheat Sheet for quick code lookups
- Continuous testing and validation
Phase 4: Advanced & Monitoring
- Complete remaining Checklist items
- Implement monitoring from Implementation Guide sections 10-12
- Establish continuous improvement process
- Document team processes
Audit Phase
- Run Lighthouse and PageSpeed Insights
- Use Checklist to identify gaps
- Prioritize issues by impact
Quick Fixes
- Implement Cheat Sheet "Top 10 Quick Wins"
- Focus on highest-impact, lowest-effort items
- Verify improvements immediately
**Systematic Improvement
- Follow Implementation Guide systematically
- Track progress with Checklist
- Use Cheat Sheet for reference
- Measure before/after for each change
Daily Development
- Keep Cheat Sheet accessible
- Reference for common patterns
- Quick issue resolution
Weekly/Sprint Reviews
- Monitor Core Web Vitals
- Check Checklist for regression
- Address new issues promptly
Monthly Audits
- Full Checklist review
- Update performance budgets
- Review monitoring data
- Plan improvements
Start with:
- Cheat Sheet - Learn the basics and quick wins
- Core Web Vitals section of Checklist
- Top 10 Quick Wins from Implementation Guide
Focus on:
- Image optimization
- Lazy loading
- Basic caching
- Minification
Start with:
- Full Checklist review
- Implementation Guide sections 1-6
- Advanced patterns in Cheat Sheet
Focus on:
- Code splitting
- Critical CSS
- Font optimization
- Service workers (basics)
Start with:
- Complete Checklist audit
- All Implementation Guide sections
- Custom solutions beyond templates
Focus on:
- Advanced service worker strategies
- Custom build optimizations
- Performance budgets and monitoring
- Team education and processes
ββββββββββββββββ
β Plan Feature β
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Reference Checklist β β Check requirements
β & Implementation β
ββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Develop with β β Use Cheat Sheet
β Cheat Sheet Open β for quick reference
ββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Test Performance β β Verify against
β (Lighthouse) β Checklist criteria
ββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Fix Issues β β Use Implementation
β β Guide solutions
ββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Deploy & Monitor β β Setup from
β β Implementation Guide
ββββββββββββββββββββββββ
Reviewer should verify:
- Images have width/height attributes (Cheat Sheet ref)
- New images are lazy loaded appropriately
- No render-blocking resources added
- JavaScript is minified and code-split
- Cache headers are appropriate
- Performance budget not exceeded
- Lighthouse score maintained or improved
Use: Quick Reference section in Cheat Sheet
Pre-commit:
# Lint and format
npm run lint
npm run format
# Quick performance check
npm run lighthouse:quickPre-merge:
# Full Lighthouse audit
npm run lighthouse:full
# Bundle size check
npm run analyze:bundle
# Performance budget check
npm run check:budgetPost-deploy:
# Real User Monitoring alert setup
# Based on Implementation Guide Section 10Reference: Implementation Guide Section 12 (Continuous Monitoring)
Track these metrics from Checklist thresholds:
Core Web Vitals (Must Pass)
- LCP: β€2.5s (75th percentile)
- CLS: β€0.1 (75th percentile)
- INP: β€200ms (75th percentile)
Supporting Metrics
- FCP: β€1.8s
- TTFB: β€800ms
- Speed Index: β€3.4s
- TBT: β€200ms
Lighthouse Scores
- Performance: 90+
- Accessibility: 90+
- Best Practices: 90+
- SEO: 90+
Real-time: Core Web Vitals via RUM Daily: Automated Lighthouse CI Weekly: PageSpeed Insights check Monthly: Full audit with Checklist
Setup: See Implementation Guide Section 10 & 12
Testing & Audit
- PageSpeed Insights: https://pagespeed.web.dev/
- Lighthouse CLI:
npm install -g lighthouse - WebPageTest: https://www.webpagetest.org/
- Chrome DevTools: Built-in
Image Optimization
- Sharp:
npm install sharp - Squoosh: https://squoosh.app/
- ImageOptim: https://imageoptim.com/
- Command-line tools (see Cheat Sheet)
Monitoring
- web-vitals:
npm install web-vitals - Google Search Console
- Chrome UX Report (CrUX)
- Custom RUM (see Implementation Guide)
Build Tools
- Webpack Bundle Analyzer
- Lighthouse CI:
npm install -g @lhci/cli - Critical CSS:
npm install critical
Analysis
- Bundlephobia: https://bundlephobia.com/
- Can I Use: https://caniuse.com/
- web.dev: https://web.dev/
For quick analysis: PageSpeed Insights + Chrome DevTools For deep analysis: WebPageTest + Lighthouse CLI For monitoring: web-vitals + Custom RUM For CI/CD: Lighthouse CI For images: Sharp (automation) or Squoosh (manual)
A website is considered optimized when:
β Core Web Vitals
- All three metrics pass at 75th percentile
- Based on 28 days of field data
- Both mobile and desktop
β Lighthouse Scores
- All categories score 90+
- Tested on both mobile and desktop
- No high-severity issues
β Checklist Coverage
- 95%+ of applicable items completed
- Documented exceptions with reasoning
- Regular review schedule established
β Monitoring Active
- Real User Monitoring implemented
- Alerts configured
- Regular reporting in place
- Team trained on metrics
β Process Integration
- Performance budgets set
- CI/CD checks active
- Code review includes performance
- Documentation updated
Priority Focus:
- Product images (largest performance impact)
- LCP optimization (hero images)
- Third-party scripts (analytics, payment)
- Mobile performance
Document Flow:
- Checklist - Category 1 (Core Web Vitals) & 2 (Performance)
- Implementation Guide - Sections 2, 3, 5, 9
- Cheat Sheet - Image optimization, lazy loading
Priority Focus:
- Text content (fonts, typography)
- Article images
- Ad performance
- Multiple page templates
Document Flow:
- Checklist - Category 2 (Performance) & 5 (SEO)
- Implementation Guide - Sections 4, 6, 9
- Cheat Sheet - Critical CSS, fonts, caching
Priority Focus:
- JavaScript bundle size
- Code splitting
- Initial load performance
- Route-based optimization
Document Flow:
- Checklist - Category 2 (Performance) & 9 (Progressive Enhancement)
- Implementation Guide - Sections 5, 11
- Cheat Sheet - JS optimization, code splitting
Priority Focus:
- First impression (LCP)
- Form performance
- Video/media
- Conversion tracking
Document Flow:
- Checklist - Category 1 (Core Web Vitals)
- Implementation Guide - Sections 2, 3, 9
- Cheat Sheet - Quick wins, YouTube facade
Diagnosis:
- Identify LCP element using Chrome DevTools
- Check if image or text
- Measure TTFB
Solutions:
- Checklist Section 1.1 (LCP Optimization)
- Implementation Guide Section 2 (Images)
- Cheat Sheet LCP checklist
Common Fixes:
- Optimize LCP image format and size
- Preload LCP element
- Eliminate render-blocking resources
- Reduce server response time
Diagnosis:
- Use Layout Shift Regions in DevTools
- Identify shifting elements
- Check image dimensions and ad slots
Solutions:
- Checklist Section 1.2 (CLS Optimization)
- Implementation Guide Section 2.2 (Images), 6 (Fonts)
- Cheat Sheet CLS checklist
Common Fixes:
- Add width/height to all images
- Reserve space for ads/embeds
- Preload fonts with font-display: swap
- Use transform for animations
Diagnosis:
- Check for long tasks in DevTools
- Identify slow event handlers
- Measure third-party script impact
Solutions:
- Checklist Section 1.3 (INP Optimization)
- Implementation Guide Section 5 (JavaScript)
- Cheat Sheet INP checklist
Common Fixes:
- Break up long tasks
- Defer non-critical JavaScript
- Optimize third-party scripts
- Use debounce/throttle
Diagnosis:
- Review Lighthouse opportunities
- Check diagnostics section
- Identify highest-impact issues
Solutions:
- Checklist - Full Category 2 review
- Implementation Guide - Systematic approach
- Cheat Sheet - Quick wins first
Common Fixes:
- Optimize images (usually biggest impact)
- Eliminate render-blocking resources
- Enable text compression
- Minimize main-thread work
"I need to understand what to do" β Start with Checklist
"I know what to do, but not how" β Use Implementation Guide
"I need a quick fix now" β Check Cheat Sheet
"I'm not sure where to start" β Read this Master Index
Simple Question
β
Cheat Sheet
β
Still Unclear?
β
Implementation Guide
β
Still Need Help?
β
Checklist (comprehensive)
β
Still Stuck?
β
External Resources:
- web.dev tutorials
- Chrome DevTools docs
- Stack Overflow
- Official documentation
Monthly:
- Review Core Web Vitals thresholds
- Update tool versions
- Add new optimization techniques
- Incorporate team feedback
Quarterly:
- Major revision check
- New browser features
- Updated best practices
- Case study additions
Annually:
- Complete document overhaul
- Remove deprecated techniques
- Major platform updates
- Performance landscape changes
Current versions of all documents are maintained together.
- Current Version: 1.0
- Last Updated: November 2025
- Next Review: February 2026
- SpeedCurve - RUM & Synthetic monitoring
- Calibre - Performance monitoring
- Cloudflare - CDN & optimization
- Cloudinary - Image optimization
To get started with this document suite:
- Read this Master Index completely
- Download/bookmark all three documents
- Print the Cheat Sheet for desk reference
- Run initial PageSpeed Insights test
- Review Checklist for full scope
- Identify top 3 issues from audit
- Use Implementation Guide for solutions
- Implement fixes from Cheat Sheet
- Re-test and measure improvement
- Set up continuous monitoring
- Schedule regular reviews
- Share documents with team
Level 1: Basics (Can achieve 70-80 scores)
- Understand Core Web Vitals
- Can implement image optimization
- Uses lazy loading
- Enables basic caching
- Mastery: Cheat Sheet memorized
Level 2: Intermediate (Can achieve 80-90 scores)
- Implements code splitting
- Extracts critical CSS
- Optimizes fonts
- Manages third-party scripts
- Mastery: Implementation Guide completed
Level 3: Advanced (Can achieve 90+ scores)
- Architects performance solutions
- Implements custom monitoring
- Establishes team processes
- Teaches others
- Mastery: Full Checklist expertise
Level 4: Expert (Consistent 95+ scores)
- Innovates new solutions
- Contributes to community
- Writes custom tooling
- Advises organizations
- Mastery: All documents internalized + industry leadership
- Lighthouse scores of PRs
- Performance budget compliance
- Time to implement optimizations
- Regression prevention
- Average site Lighthouse score
- Core Web Vitals pass rate
- Performance issue resolution time
- Knowledge sharing sessions held
- Page load time impact on conversions
- Mobile vs desktop performance gap
- SEO ranking improvements
- User engagement metrics
From across all documents:
- Mobile First - Always optimize mobile first, desktop is easier
- Measure Everything - You can't improve what you don't measure
- Automate Testing - Manual testing doesn't scale
- Set Budgets - Prevent performance regression
- Optimize Images First - Usually the biggest wins
- Lazy Load Smartly - Not above the fold
- Critical CSS Only - Keep inline CSS minimal
- Code Split Aggressively - Users don't need everything at once
- Monitor Continuously - Performance degrades over time
- Educate Team - Performance is everyone's responsibility
- Week 1: Implement all items from Cheat Sheet "Top 10 Quick Wins"
- Week 2-3: Work through Implementation Guide systematically
- Week 4: Complete remaining Checklist items
- Ongoing: Monitor with tools from Implementation Guide Section 10-12
- Don't skip the planning phase
- Measure before and after every change
- Test on real devices and networks
- Document what works for your team
- Share knowledge across the organization
- Review and update quarterly
- Integrate performance into development culture
- Make it part of code review
- Automate everything possible
- Celebrate performance wins
- Learn from performance issues
- Stay updated with industry changes
Remember: Web performance is a journey, not a destination. These documents provide the roadmap, but continuous improvement and monitoring are essential for long-term success.
Document Suite Version: 1.0
Master Index Version: 1.0
Last Updated: November 2025
Next Review: February 2026
Maintained by: Your Development Team
Questions? Reference the appropriate document or external resources listed above
End of Master Index
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | β€2.5s | 2.5-4.0s | >4.0s |
| CLS (Cumulative Layout Shift) | β€0.1 | 0.1-0.25 | >0.25 |
| INP (Interaction to Next Paint) | β€200ms | 200-500ms | >500ms |
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| FCP (First Contentful Paint) | β€1.8s | 1.8-3.0s | >3.0s |
| TTFB (Time to First Byte) | β€800ms | 800-1800ms | >1800ms |
| Speed Index | β€3.4s | 3.4-5.8s | >5.8s |
| TBT (Total Blocking Time) | β€200ms | 200-600ms | >600ms |
| TTI (Time to Interactive) | β€3.8s | 3.8-7.3s | >7.3s |
- Good: 90-100
- Needs Improvement: 50-89
- Poor: 0-49
Resource: Web Vitals Initiative
-
Optimize server infrastructure
- Target: TTFB β€800ms
- Use CDN for static assets
- Implement caching strategies (Redis, Memcached)
- Optimize database queries
- Resource: Optimize TTFB
-
Enable HTTP/2 or HTTP/3
- Multiplexing support for parallel requests
- Header compression
- Server push capabilities
-
Eliminate render-blocking resources
- Defer non-critical JavaScript
- Inline critical CSS (<14KB)
- Use media attributes for non-critical CSS
- Resource: Eliminate render-blocking resources
-
Optimize Critical Rendering Path
- Minimize critical resource count
- Reduce critical resource size
- Shorten critical path length
-
Optimize images for LCP
- Use appropriate formats (WebP, AVIF)
- Implement responsive images with srcset
- Compress images (aim for <200KB for hero images)
- Priority hint:
<img fetchpriority="high"> - Resource: Optimize LCP
-
Preload critical resources
<link rel="preload">for LCP image/font- Preconnect to required origins
- DNS prefetch for third-party domains
- Minimize JavaScript execution time
- Code splitting and lazy loading
- Tree shaking unused code
- Use web workers for heavy computations
- Target: Main thread work <3.8s
- Set explicit width and height attributes
- Add width/height to all
<img>tags - Use aspect-ratio CSS for responsive images
- Reserve space with aspect-ratio boxes
- Resource: Optimize CLS
- Add width/height to all
- Reserve space for dynamic content
- Define minimum dimensions for ad slots
- Use placeholder for embeds
- Set aspect ratios for iframes
- Load content above the fold statically
- Avoid inserting content above existing content
- Use transform animations instead of layout properties
- Preload and reserve space for web fonts
- Optimize font loading strategy
- Use
font-display: swaporoptional - Preload critical fonts
- Self-host fonts when possible
- Subset fonts to include only needed characters
- Resource: Font best practices
- Use
- Use transform and opacity only
- Avoid animating: top, left, width, height, margin
- Use CSS transforms (translateX/Y, scale, rotate)
- Use will-change sparingly
- Minimize main thread blocking
- Break up long tasks (>50ms)
- Use requestIdleCallback for non-urgent work
- Implement code splitting
- Reduce JavaScript payload
- Resource: Optimize INP
- Optimize interaction handlers
- Debounce/throttle frequent events
- Use passive event listeners
- Avoid forced synchronous layouts
- Minimize DOM manipulation
- Reduce render complexity
- Simplify CSS selectors
- Reduce scope and complexity of style calculations
- Minimize layout thrashing
- Use CSS containment
- Audit and optimize third-party impact
- Load third-party scripts asynchronously
- Use facade patterns for heavy embeds
- Implement third-party script loading strategy
- Monitor third-party performance budget
-
Minimize JavaScript bundle size
- Target: <300KB gzipped for initial bundle
- Remove unused code (tree shaking)
- Analyze bundle with webpack-bundle-analyzer
- Split code by route/component
- Resource: Reduce JavaScript payloads
-
Minify and compress JavaScript
- Use Terser or esbuild for minification
- Enable gzip/brotli compression
- Remove console logs in production
-
Implement proper loading patterns
- Use
asyncfor independent scripts - Use
deferfor DOM-dependent scripts - Inline critical JavaScript (<2KB)
- Load non-critical scripts after main content
- Use
-
Leverage code splitting
- Route-based splitting
- Component-based lazy loading
- Dynamic imports for heavy features
- Vendor bundle separation
-
Optimize JavaScript execution
- Avoid long tasks (>50ms)
- Use Web Workers for CPU-intensive tasks
- Implement virtual scrolling for long lists
- Debounce expensive operations
- Resource: Optimize JavaScript execution
-
Reduce JavaScript boot-up time
- Parse and compile cost reduction
- Minimize polyfills (use module/nomodule pattern)
- Use modern JavaScript (ES2015+) for modern browsers
-
React optimization
- Use React.lazy() and Suspense
- Implement React.memo for expensive components
- Use useMemo and useCallback appropriately
- Avoid inline function definitions in render
-
Vue optimization
- Use async components
- Implement v-once for static content
- Use v-show vs v-if appropriately
-
Angular optimization
- Use OnPush change detection
- Implement lazy loading for modules
- Use trackBy for ngFor
-
Eliminate render-blocking CSS
- Inline critical CSS (<14KB)
- Load non-critical CSS asynchronously
- Use media queries to defer non-matching CSS
- Resource: Extract critical CSS
-
Minimize CSS payload
- Remove unused CSS (PurgeCSS, UnCSS)
- Minify CSS
- Target: <50KB critical CSS
-
Optimize CSS selectors
- Reduce selector complexity
- Avoid universal selectors
- Minimize nesting depth (<3 levels)
- Use specific selectors over descendant
-
Use efficient CSS properties
- Prefer CSS transforms over layout properties
- Use CSS containment
- Avoid expensive properties (box-shadow, filter on large areas)
- Use will-change sparingly
-
Optimize font loading
- Use font-display: swap or optional
- Preload critical fonts
- Subset fonts
- Use variable fonts when appropriate
-
Use CSS preprocessing efficiently
- Remove unused mixins and variables
- Optimize output configuration
- Use CSS variables for theming
-
Use next-gen image formats
- WebP for broad support
- AVIF for best compression (with WebP fallback)
- Progressive JPEG for large images
- Resource: Serve images in next-gen formats
-
Properly size images
- Serve responsive images (srcset, sizes)
- Match image dimensions to display size
- Target: No images >2x display size
- Resource: Properly size images
-
Compress images efficiently
- Target compression: 85-90% quality for JPEG
- Use tools: ImageOptim, Squoosh, Sharp
- Automate with build process
- Target: Hero images <200KB, other images <100KB
-
Implement lazy loading
- Use native lazy loading:
loading="lazy" - Lazy load offscreen images
- Eager load above-the-fold images
- Resource: Browser-level lazy loading
- Use native lazy loading:
-
Prioritize critical images
- Use
fetchpriority="high"for LCP image - Preload hero images
- Avoid lazy loading above-the-fold images
- Use
-
Use CDN for images
- Image CDN with automatic optimization
- Dynamic resizing and format selection
- Consider: Cloudinary, Imgix, Cloudflare Images
-
Implement responsive images
- Use srcset for resolution switching
- Use picture element for art direction
- Provide appropriate sizes attribute
-
Optimize font loading
- Preload critical fonts
- Use font-display: swap (or optional for body text)
- Limit number of font weights and styles
- Resource: Best practices for fonts
-
Self-host fonts when possible
- Reduce DNS lookups
- Better caching control
- Eliminate third-party requests
-
Optimize font files
- Use WOFF2 format (best compression)
- Subset fonts (include only needed characters)
- Use variable fonts to reduce file count
- Target: <50KB per font file
-
Limit font usage
- Maximum 2-3 font families
- Limit to essential weights and styles
- Use system fonts for non-critical text
-
Implement effective caching headers
- Long cache for immutable assets (1 year)
- Use cache-busting for versioned files
- Implement stale-while-revalidate
- Resource: HTTP caching
-
Cache-Control headers
Static assets: Cache-Control: public, max-age=31536000, immutable HTML: Cache-Control: no-cache API: Cache-Control: private, max-age=3600
- Implement service worker
- Cache static assets
- Implement offline fallback
- Use workbox for easier implementation
- Resource: Service workers
- Use resource hints strategically
- Preconnect to critical third-party origins
- DNS-prefetch for third-party domains
- Preload critical resources
- Prefetch for likely navigation
- Resource: Resource hints
- Enable text compression
- Enable Brotli compression (level 4-6)
- Fallback to gzip (level 6-9)
- Compress all text assets (HTML, CSS, JS, JSON, SVG)
- Resource: Enable text compression
-
Audit third-party impact
- Identify blocking third-party scripts
- Measure performance impact
- Remove unnecessary third-party code
- Resource: Third-party JavaScript
-
Optimize third-party loading
- Load asynchronously
- Use facades for heavy embeds (YouTube, maps)
- Implement lazy loading for below-fold embeds
- Self-host when beneficial
- Optimize analytics implementation
- Load analytics asynchronously
- Use minimal tracking code
- Consider server-side analytics
- Batch analytics events
-
Optimize server response time
- Target: TTFB <800ms
- Use fast hosting infrastructure
- Implement server-side caching
- Optimize database queries
-
Enable HTTP/2 or HTTP/3
- Multiplexing benefits
- Header compression
- Server push for critical resources
-
Use CDN effectively
- Global edge network
- Automatic asset optimization
- DDoS protection
- Consider: Cloudflare, Fastly, AWS CloudFront
-
Implement edge caching
- Cache static assets at edge
- Use edge workers for dynamic content
- Implement stale-while-revalidate
-
Provide alt text for images
- Descriptive alt text for informative images
- Empty alt="" for decorative images
- No "image of" or "picture of" prefixes
- Resource: Image accessibility
-
Ensure sufficient color contrast
- Minimum 4.5:1 for normal text
- Minimum 3:1 for large text (18pt+)
- Use contrast checker tools
- Resource: Color contrast
-
Provide captions and transcripts
- Captions for video content
- Transcripts for audio content
- Synchronized captions
-
Use legible font sizes
- Minimum 16px for body text
- Scalable text (no fixed pixel heights)
- Responsive typography
-
Avoid text in images
- Use actual text instead of images of text
- Exception: logos and essential graphics
-
Ensure full keyboard accessibility
- All interactive elements keyboard accessible
- Logical tab order
- Visible focus indicators
- No keyboard traps
- Resource: Keyboard access
-
Provide skip navigation
- Skip to main content link
- Skip repetitive navigation blocks
- Adequate touch target size
- Minimum 48Γ48px for mobile
- Minimum 44Γ44px for desktop
- Sufficient spacing between targets (8px)
- Resource: Touch targets
-
Use proper HTML structure
- Semantic elements (header, nav, main, article, aside, footer)
- Proper heading hierarchy (h1-h6)
- Meaningful landmark roles
- Resource: Semantic HTML
-
Proper form labels
- Label elements for all inputs
- Descriptive labels
- Associated with inputs (for/id)
- Resource: Form labels
-
Declare document language
<html lang="en">attribute- Use appropriate language codes
- Resource: Document language
-
Clear and simple language
- Appropriate reading level
- Clear instructions
- Descriptive link text
-
Implement ARIA correctly
- Use native HTML when possible
- ARIA only when necessary
- Valid ARIA attributes
- No conflicting ARIA roles
- Resource: ARIA practices
-
Provide ARIA labels
- aria-label for icon buttons
- aria-labelledby for complex widgets
- aria-describedby for additional context
- Manage focus appropriately
- Focus on modal open
- Focus trap in modals
- Return focus on modal close
- Visible focus indicators
- Use HTTPS exclusively
- Valid SSL certificate
- Redirect HTTP to HTTPS
- HSTS header enabled
- No mixed content
- Resource: HTTPS best practices
- Implement CSP
- Define trusted content sources
- Prevent XSS attacks
- Use nonce or hash for inline scripts
- Resource: Content Security Policy
- Request permissions appropriately
- Only request needed permissions
- Request at time of use
- Provide context for requests
-
Avoid console errors
- No JavaScript errors in console
- Remove console.log in production
- Proper error handling
-
Use modern JavaScript
- ES2015+ features
- Avoid deprecated APIs
- Use strict mode
-
Keep dependencies updated
- Regular security updates
- Latest stable versions
- Audit for known vulnerabilities
- Resource: npm audit
-
Avoid known vulnerable libraries
- Regular security scans
- Replace vulnerable dependencies
- Monitor security advisories
-
Test across browsers
- Chrome, Firefox, Safari, Edge
- Mobile browsers (iOS Safari, Chrome Android)
- Use feature detection, not browser detection
-
Provide fallbacks
- Progressive enhancement
- Polyfills for missing features
- Graceful degradation
- Use standards-compliant code
- Valid HTML
- Valid CSS
- Standard JavaScript APIs
- Prevent layout shifts
- Reserve space for dynamic content
- Fixed dimensions for images/ads
- Avoid inserting content above viewport
- Implement loading states
- Skeleton screens
- Progress indicators
- Smooth transitions
- Optimistic UI updates
-
Provide descriptive title
- Unique title per page
- 50-60 characters
- Include primary keywords
- Format: "Page Title | Site Name"
- Resource: Document title
-
Write compelling meta descriptions
- 150-160 characters
- Include target keywords
- Unique per page
- Action-oriented
- Resource: Meta description
-
Use Open Graph tags
- og:title, og:description, og:image
- Twitter card tags
- Optimize for social sharing
-
Implement schema markup
- Use JSON-LD format
- Relevant schema types (Article, Product, FAQ, etc.)
- Validate with Schema.org validator
- Resource: Structured data
-
Rich snippets optimization
- Product schema with reviews
- Article schema with publication date
- FAQ schema for common questions
-
Ensure pages are indexable
- No "noindex" on important pages
- No blocking in robots.txt
- Check robot meta tags
- Resource: Crawlable links
-
Create and submit sitemap
- XML sitemap with all important pages
- Submit to Google Search Console
- Update regularly
- Include lastmod dates
- Optimize robots.txt
- Allow crawling of important pages
- Block irrelevant pages
- Include sitemap location
- Resource: Robots.txt
-
Use descriptive anchor text
- Avoid "click here" or generic text
- Describe destination page
- Use keywords naturally
- Resource: Descriptive link text
-
Ensure links are crawlable
- Use
<a href="">tags (not JavaScript-only) - Valid href attributes
- No broken links
- Resource: Crawlable links
- Use
-
Implement canonical URLs
- Self-referencing canonical on each page
- Resolve duplicate content issues
- Use absolute URLs
- Resource: Canonical links
-
Set up hreflang for multilingual sites
- Indicate language/region variations
- Bi-directional references
- Use correct language codes
- Resource: Hreflang
-
Use responsive design
- Viewport meta tag configured
- Mobile-friendly layouts
- Readable font sizes (16px+)
- Resource: Mobile-friendly test
-
Avoid mobile usability issues
- No Flash content
- Adequate tap target sizes
- No horizontal scrolling
- Content sized to viewport
- Return appropriate status codes
- 200 for successful pages
- 301 for permanent redirects
- 404 for not found
- Avoid soft 404s
- Resource: HTTP status codes
- Maintain good Core Web Vitals
- Pass Core Web Vitals assessment
- Monitor field data regularly
- Fix performance issues promptly
-
Regular Lighthouse audits
- Test on mobile and desktop
- Use CI/CD integration
- Set performance budgets
- Resource: Lighthouse CI
-
Test with throttling
- Slow 3G network simulation
- CPU throttling (4x slowdown)
- Mid-tier device simulation (Moto G4)
-
Implement RUM
- Track Core Web Vitals
- Monitor 75th percentile values
- Segment by device, connection, geography
- Resource: Web Vitals library
-
Use PageSpeed Insights regularly
- Check field data (CrUX)
- Monitor both origin and URL-level data
- Track trends over time
-
Set and enforce budgets
- JavaScript bundle size limits
- Image size limits
- Total page weight limits
- Timing metric budgets
- Resource: Performance budgets
-
Automated budget monitoring
- CI/CD integration
- Fail builds that exceed budgets
- Alert on regression
- Test optimization impact
- Measure before and after
- Test with real users
- Monitor business metrics
- Iterate based on data
-
Test on actual devices
- iOS devices (iPhone, iPad)
- Android devices (various manufacturers)
- Different screen sizes
- Different browsers
-
Use device emulation
- Chrome DevTools device mode
- BrowserStack or similar
- Test touch interactions
- Test orientation changes
-
Google PageSpeed Insights
- Field and lab data
- Mobile and desktop
- Resource: PageSpeed Insights
-
Google Search Console
- Core Web Vitals report
- Mobile usability report
- Indexing status
- Resource: Search Console
-
Chrome UX Report
- Historical field data
- Origin-level aggregation
- BigQuery integration
- Resource: CrUX
-
WebPageTest
- Detailed waterfall analysis
- Filmstrip view
- Multiple location testing
- Resource: WebPageTest
-
Lighthouse CI
- Automated testing
- GitHub integration
- Performance tracking
- Resource: Lighthouse CI
-
Optimize for slower networks
- Test on 3G/4G networks
- Implement adaptive loading
- Use connection-aware code (Network Information API)
- Provide offline functionality
-
Reduce data usage
- Compress all assets
- Lazy load images and video
- Use data-saver-friendly patterns
- Optimize video streaming
-
Touch optimization
- Adequate touch targets (48Γ48px minimum)
- Implement touch feedback
- Support common gestures
- Avoid hover-dependent UI
-
Mobile viewport configuration
<meta name="viewport" content="width=device-width, initial-scale=1">- No user-scalable=no (unless absolutely necessary)
- Prevent zoom on input focus
-
Mobile-first CSS
- Start with mobile styles
- Use min-width media queries
- Progressive enhancement
- Conditional loading of desktop assets
-
Optimize for desktop capabilities
- Leverage higher bandwidth
- Provide enhanced experiences
- Use higher resolution images
- Enable advanced features
-
Desktop-specific features
- Hover states and interactions
- Keyboard shortcuts
- Multi-column layouts
- Advanced animations
- Design for common desktop sizes
- 1920Γ1080 (Full HD)
- 1366Γ768
- 1440Γ900
- 1280Γ720
- Support ultra-wide monitors
- Ensure baseline functionality
- Core features work without JavaScript
- Content accessible with CSS disabled
- Graceful degradation
- Progressive enhancement approach
- Layer enhancements progressively
- Feature detection (not browser detection)
- Polyfills for older browsers
- Modern features for capable browsers
- Use @supports for CSS features
- Prioritize visible content
- Inline critical CSS
- Preload hero images
- Defer below-the-fold resources
- Eliminate render-blocking resources
- Provide instant feedback
- Loading indicators
- Skeleton screens
- Smooth transitions
- Progress feedback
-
Implement Web Vitals measurement
import {onCLS, onFCP, onLCP, onTTFB, onINP} from 'web-vitals'; onCLS(console.log); onFCP(console.log); onLCP(console.log); onTTFB(console.log); onINP(console.log);
- Resource: web-vitals library
-
Send data to analytics
- Track in Google Analytics 4
- Custom analytics solution
- RUM service (SpeedCurve, Calibre, etc.)
- Track business-specific metrics
- Time to key content
- Time to interactive for critical features
- Custom user timing marks
- Error tracking
- Enable compression (Brotli/gzip)
- Implement lazy loading for images
- Add width/height to images
- Minify CSS/JS
- Optimize images (WebP, compression)
- Add meta descriptions
- Fix obvious accessibility issues
- Implement code splitting
- Optimize LCP element
- Eliminate render-blocking resources
- Implement effective caching
- Add resource hints
- Implement font optimization
- Add structured data
- Implement service worker
- Optimize for INP
- Third-party script optimization
- Advanced image optimization (CDN, responsive images)
- Implement performance monitoring
- Set performance budgets
- Advanced SEO optimizations
- Monitor Core Web Vitals
- Regular performance audits
- A/B testing optimizations
- Keep dependencies updated
- Iterate based on real user data
- Address new recommendations
- Stay updated with best practices
- β LCP: β€2.5s (75th percentile)
- β CLS: β€0.1 (75th percentile)
- β INP: β€200ms (75th percentile)
- β Performance: 90+
- β Accessibility: 90+
- β Best Practices: 90+
- β SEO: 90+
- β FCP: β€1.8s
- β TTFB: β€800ms
- β Speed Index: β€3.4s
- β TBT: β€200ms
// Example: Automated PageSpeed testing with Puppeteer/Playwright
const { chromium } = require('playwright');
const lighthouse = require('lighthouse');
async function runPageSpeedTest(url) {
const browser = await chromium.launch();
const { lhr } = await lighthouse(url, {
port: new URL(browser.wsEndpoint()).port,
output: 'json',
onlyCategories: ['performance'],
});
// Extract Core Web Vitals
const metrics = {
lcp: lhr.audits['largest-contentful-paint'].numericValue,
cls: lhr.audits['cumulative-layout-shift'].numericValue,
tbt: lhr.audits['total-blocking-time'].numericValue,
fcp: lhr.audits['first-contentful-paint'].numericValue,
};
await browser.close();
return metrics;
}- Run Lighthouse on every pull request
- Fail builds that don't meet performance budgets
- Generate performance reports
- Track performance over time
- Alert on regressions
- Real-time Core Web Vitals tracking
- Daily performance reports
- Alert on threshold violations
- Geographic performance monitoring
- Device-specific tracking
Use this condensed checklist for quick reference:
- All Core Web Vitals passing (LCP, CLS, INP)
- All Lighthouse scores 90+
- Images optimized (WebP/AVIF, lazy loading, dimensions)
- JavaScript optimized (splitting, minification, defer/async)
- CSS optimized (critical CSS, removal of unused)
- Fonts optimized (preload, font-display, subsetting)
- Caching implemented (long-term for static assets)
- Compression enabled (Brotli/gzip)
- HTTPS everywhere
- Accessibility compliance (WCAG 2.1 AA)
- SEO essentials (titles, descriptions, structured data)
- Performance monitoring active
- Mobile responsiveness confirmed
- Cross-browser testing completed
Document Version: 1.0
Based on: Google PageSpeed Insights v5 Documentation
Last Updated: November 2025
Maintenance: Review quarterly and after major web platform updates
- Prioritization: Focus on Core Web Vitals first, as they have the most significant impact on user experience and SEO
- Testing: Always test on real devices and networks, not just emulators
- Iteration: Performance optimization is continuousβmeasure, optimize, measure again
- Context: Some optimizations may not apply to all sitesβuse judgment based on your specific context
- Trade-offs: Balance performance with functionality and business requirements
- Documentation: Document all optimizations and their impact for future reference
- Team Education: Ensure the entire team understands performance best practices
This checklist is a living document. As web standards and best practices evolve, update accordingly.
| Priority | Optimization | Impact | Effort | Time to Implement |
|---|---|---|---|---|
| P0 | Enable compression | High | Low | 30 min |
| P0 | Optimize images | High | Medium | 2-4 hours |
| P0 | Lazy load images | High | Low | 1 hour |
| P0 | Add image dimensions | High | Low | 1 hour |
| P0 | Minify assets | High | Low | 1 hour |
| P1 | Code splitting | High | Medium | 4-8 hours |
| P1 | Critical CSS | High | Medium | 4-6 hours |
| P1 | Font optimization | Medium | Low | 2-3 hours |
| P1 | Resource hints | Medium | Low | 2 hours |
| P2 | Service Worker | Medium | High | 8-16 hours |
| P2 | Third-party optimization | Medium | Medium | 4-8 hours |
# /etc/nginx/nginx.conf
http {
# Enable Brotli
brotli on;
brotli_comp_level 6; # 0-11, 6 is recommended for balance
brotli_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/x-javascript
application/xml
application/xml+rss
application/xhtml+xml
application/rss+xml
application/atom+xml
image/svg+xml
font/opentype
font/ttf
font/eot
font/otf;
# Fallback to gzip
gzip on;
gzip_comp_level 6;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/atom+xml image/svg+xml;
}# .htaccess or httpd.conf
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css text/javascript
AddOutputFilterByType BROTLI_COMPRESS application/javascript application/json application/xml
AddOutputFilterByType BROTLI_COMPRESS image/svg+xml font/opentype font/ttf font/eot font/otf
BrotliCompressionQuality 6
</IfModule>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json application/xml
AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>1. Navigate to Speed > Optimization
2. Enable "Brotli" compression
3. Enable "Auto Minify" for HTML, CSS, JS
# Check if Brotli is enabled
curl -H "Accept-Encoding: br" -I https://your-site.com
# Check if gzip is enabled
curl -H "Accept-Encoding: gzip" -I https://your-site.com
# Check compression ratio
curl -H "Accept-Encoding: gzip" https://your-site.com | wc -c
curl https://your-site.com | wc -c// build-scripts/optimize-images.js
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
async function convertToWebP(inputPath, outputPath) {
await sharp(inputPath)
.webp({ quality: 85, effort: 6 })
.toFile(outputPath);
}
async function convertToAVIF(inputPath, outputPath) {
await sharp(inputPath)
.avif({ quality: 80, effort: 6 })
.toFile(outputPath);
}
// Process all images in directory
async function processImages(directory) {
const files = fs.readdirSync(directory);
for (const file of files) {
if (/\.(jpg|jpeg|png)$/i.test(file)) {
const inputPath = path.join(directory, file);
const baseName = path.parse(file).name;
// Generate WebP
await convertToWebP(
inputPath,
path.join(directory, `${baseName}.webp`)
);
// Generate AVIF
await convertToAVIF(
inputPath,
path.join(directory, `${baseName}.avif`)
);
console.log(`Processed: ${file}`);
}
}
}
processImages('./public/images');# Convert to WebP
find ./images -name "*.jpg" -o -name "*.png" | while read img; do
cwebp -q 85 "$img" -o "${img%.*}.webp"
done
# Convert to AVIF
find ./images -name "*.jpg" -o -name "*.png" | while read img; do
avifenc --min 0 --max 63 -a end-usage=q -a cq-level=23 "$img" "${img%.*}.avif"
done<picture>
<source
type="image/avif"
srcset="
/images/hero-400.avif 400w,
/images/hero-800.avif 800w,
/images/hero-1200.avif 1200w,
/images/hero-1600.avif 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px">
<source
type="image/webp"
srcset="
/images/hero-400.webp 400w,
/images/hero-800.webp 800w,
/images/hero-1200.webp 1200w,
/images/hero-1600.webp 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px">
<img
src="/images/hero-800.jpg"
srcset="
/images/hero-400.jpg 400w,
/images/hero-800.jpg 800w,
/images/hero-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px"
alt="Hero image description"
width="1200"
height="675"
loading="lazy"
decoding="async">
</picture>{
"scripts": {
"optimize:images": "node scripts/optimize-images.js",
"prebuild": "npm run optimize:images"
},
"devDependencies": {
"sharp": "^0.32.0",
"imagemin": "^8.0.1",
"imagemin-mozjpeg": "^10.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-webp": "^7.0.0"
}
}import Image from 'next/image';
export default function OptimizedImage() {
return (
<Image
src="/images/hero.jpg"
alt="Hero image"
width={1200}
height={675}
priority={true} // For LCP image
quality={85}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
);
}<!-- For images below the fold -->
<img
src="/images/content.jpg"
alt="Content image"
width="800"
height="600"
loading="lazy"
decoding="async">
<!-- For critical above-the-fold images -->
<img
src="/images/hero.jpg"
alt="Hero image"
width="1200"
height="675"
loading="eager"
fetchpriority="high">
<!-- For iframes -->
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
loading="lazy"
title="Video title">
</iframe>// utils/lazy-load.js
class LazyLoader {
constructor(options = {}) {
this.options = {
rootMargin: '50px',
threshold: 0.01,
...options
};
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
this.options
);
}
observe(elements) {
elements.forEach(element => {
this.observer.observe(element);
});
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadElement(entry.target);
this.observer.unobserve(entry.target);
}
});
}
loadElement(element) {
if (element.tagName === 'IMG') {
const src = element.dataset.src;
const srcset = element.dataset.srcset;
if (srcset) element.srcset = srcset;
if (src) element.src = src;
} else if (element.tagName === 'IFRAME') {
element.src = element.dataset.src;
}
element.classList.remove('lazy');
element.classList.add('loaded');
}
}
// Initialize
const lazyLoader = new LazyLoader();
const lazyImages = document.querySelectorAll('img.lazy, iframe.lazy');
lazyLoader.observe(lazyImages);// components/LazyImage.jsx
import { useState, useEffect, useRef } from 'react';
export default function LazyImage({ src, alt, width, height, className }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isInView, setIsInView] = useState(false);
const imgRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsInView(true);
observer.disconnect();
}
},
{ rootMargin: '50px' }
);
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div
ref={imgRef}
style={{
width,
height,
position: 'relative',
backgroundColor: '#f0f0f0'
}}
className={className}
>
{isInView && (
<img
src={src}
alt={alt}
width={width}
height={height}
onLoad={() => setIsLoaded(true)}
style={{
opacity: isLoaded ? 1 : 0,
transition: 'opacity 0.3s'
}}
/>
)}
</div>
);
}// scripts/extract-critical.js
const critical = require('critical');
const fs = require('fs');
const path = require('path');
async function extractCritical() {
const pages = [
{ url: 'index.html', output: 'index-critical.html' },
{ url: 'about.html', output: 'about-critical.html' },
{ url: 'product.html', output: 'product-critical.html' }
];
for (const page of pages) {
try {
const { html } = await critical.generate({
src: page.url,
target: {
html: page.output,
css: `${path.parse(page.output).name}-critical.css`
},
width: 1300,
height: 900,
dimensions: [
{ width: 375, height: 667 }, // Mobile
{ width: 768, height: 1024 }, // Tablet
{ width: 1300, height: 900 } // Desktop
],
inline: true,
extract: true,
penthouse: {
timeout: 60000
}
});
console.log(`Generated critical CSS for ${page.url}`);
} catch (err) {
console.error(`Error processing ${page.url}:`, err);
}
}
}
extractCritical();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title</title>
<!-- Inline Critical CSS -->
<style>
/* Critical styles for above-the-fold content */
body { margin: 0; font-family: sans-serif; }
.header { background: #000; color: #fff; padding: 1rem; }
.hero { height: 100vh; background: url('/hero-thumb.jpg'); }
/* ... more critical styles ... */
</style>
<!-- Preload full stylesheet -->
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
<!-- Async CSS loading script -->
<script>
!function(e){"use strict";var t=function(t,n,r,o){var i,a=e.document,d=a.createElement("link");if(n)i=n;else{var f=(a.body||a.getElementsByTagName("head")[0]).childNodes;i=f[f.length-1]}var l=a.styleSheets;if(o)for(var s in o)o.hasOwnProperty(s)&&d.setAttribute(s,o[s]);d.rel="stylesheet",d.href=t,d.media="only x",function e(t){if(a.body)return t();setTimeout(function(){e(t)})}(function(){i.parentNode.insertBefore(d,n?i:i.nextSibling)});var u=function(e){for(var t=d.href,n=l.length;n--;)if(l[n].href===t)return e();setTimeout(function(){u(e)})};function c(){d.addEventListener&&d.removeEventListener("load",c),d.media=r||"all"}return d.addEventListener&&d.addEventListener("load",c),(d.onloadcssdefined=u)(c),d};"undefined"!=typeof exports?exports.loadCSS=t:e.loadCSS=t}("undefined"!=typeof global?global:this);
</script>
</head>
<body>
<!-- Page content -->
</body>
</html>// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlCriticalWebpackPlugin = require('html-critical-webpack-plugin');
module.exports = {
// ... other config
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new HtmlCriticalWebpackPlugin({
base: path.resolve(__dirname, 'dist'),
src: 'index.html',
dest: 'index.html',
inline: true,
minify: true,
extract: true,
width: 1300,
height: 900,
penthouse: {
blockJSRequests: false,
}
})
]
};// webpack.config.js
module.exports = {
entry: {
main: './src/index.js',
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 20000,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// Get package name
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
return `npm.${packageName.replace('@', '')}`;
},
priority: 10,
},
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
},
},
},
runtimeChunk: 'single',
},
};// React component with lazy loading
import React, { lazy, Suspense } from 'react';
// Lazy load heavy components
const HeavyChart = lazy(() => import('./components/HeavyChart'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
const ImageGallery = lazy(() => import('./components/ImageGallery'));
function App() {
return (
<div>
<Header />
<Suspense fallback={<LoadingSpinner />}>
<HeavyChart data={chartData} />
</Suspense>
<Suspense fallback={<div>Loading video...</div>}>
<VideoPlayer src="/video.mp4" />
</Suspense>
<Suspense fallback={<div>Loading gallery...</div>}>
<ImageGallery images={images} />
</Suspense>
</div>
);
}
// Route-based code splitting
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Products = lazy(() => import('./pages/Products'));
function Routes() {
return (
<Suspense fallback={<PageLoader />}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/products" component={Products} />
</Switch>
</Suspense>
);
}<!-- Critical scripts - defer -->
<script src="/js/critical.js" defer></script>
<!-- Non-critical scripts - async -->
<script src="/js/analytics.js" async></script>
<!-- Third-party scripts - async with loading strategy -->
<script>
// Load third-party script after page load
window.addEventListener('load', () => {
const script = document.createElement('script');
script.src = 'https://example.com/widget.js';
script.async = true;
document.body.appendChild(script);
});
</script>
<!-- Or use requestIdleCallback -->
<script>
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
const script = document.createElement('script');
script.src = 'https://example.com/widget.js';
document.body.appendChild(script);
});
} else {
// Fallback for browsers without requestIdleCallback
setTimeout(() => {
const script = document.createElement('script');
script.src = 'https://example.com/widget.js';
document.body.appendChild(script);
}, 1000);
}
</script>// package.json
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
// webpack.config.js
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
pure_funcs: ['console.log', 'console.info', 'console.debug']
},
mangle: true,
},
}),
],
},
};
// Import only what you need
import { debounce, throttle } from 'lodash-es'; // β
Good
// import _ from 'lodash'; // β Imports entire library<!-- Preload critical fonts -->
<link
rel="preload"
href="/fonts/primary-font-regular.woff2"
as="font"
type="font/woff2"
crossorigin>
<link
rel="preload"
href="/fonts/primary-font-bold.woff2"
as="font"
type="font/woff2"
crossorigin>
<style>
@font-face {
font-family: 'PrimaryFont';
src: url('/fonts/primary-font-regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap; /* or 'optional' for better CLS */
}
@font-face {
font-family: 'PrimaryFont';
src: url('/fonts/primary-font-bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
</style># Using pyftsubset (part of fonttools)
pip install fonttools brotli
# Subset to include only Latin characters
pyftsubset font.ttf \
--unicodes="U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD" \
--flavor=woff2 \
--output-file=font-subset.woff2
# Or use glyphhanger
npm install -g glyphhanger
# Generate subset based on actual usage on your site
glyphhanger https://your-site.com --subset=font.ttf --formats=woff2@font-face {
font-family: 'InterVariable';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900; /* Variable weight range */
font-display: swap;
}
body {
font-family: 'InterVariable', sans-serif;
font-weight: 400;
}
h1 {
font-weight: 700;
/* Same font file, different weight */
}// Detect when fonts are loaded
if ('fonts' in document) {
document.fonts.ready.then(() => {
document.body.classList.add('fonts-loaded');
});
}
// Or use Font Face Observer library
const FontFaceObserver = require('fontfaceobserver');
const font = new FontFaceObserver('PrimaryFont');
font.load().then(() => {
document.documentElement.classList.add('fonts-loaded');
}).catch(() => {
// Font failed to load
console.warn('Font loading failed');
});# /etc/nginx/sites-available/your-site
server {
listen 443 ssl http2;
server_name your-site.com;
# HTML - no cache
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# CSS and JavaScript - versioned files (immutable)
location ~* \.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# Images - long cache
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# Fonts - long cache with crossorigin
location ~* \.(woff|woff2|ttf|eot)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
# API responses - short cache
location /api/ {
add_header Cache-Control "private, max-age=300, stale-while-revalidate=60";
}
}# .htaccess
<IfModule mod_expires.c>
ExpiresActive On
# HTML
ExpiresByType text/html "access plus 0 seconds"
# CSS and JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
# Images
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
# Fonts
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
</IfModule>
<IfModule mod_headers.c>
# Immutable cache for versioned files
<FilesMatch "\.(css|js|jpg|jpeg|png|webp|woff2|woff)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# No cache for HTML
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
</FilesMatch>
</IfModule>// service-worker.js
const CACHE_VERSION = 'v1';
const CACHE_STATIC = `static-${CACHE_VERSION}`;
const CACHE_DYNAMIC = `dynamic-${CACHE_VERSION}`;
const STATIC_ASSETS = [
'/',
'/styles/main.css',
'/js/app.js',
'/fonts/primary-font.woff2',
'/images/logo.svg'
];
// Install event - cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_STATIC)
.then(cache => cache.addAll(STATIC_ASSETS))
.then(() => self.skipWaiting())
);
});
// Activate event - clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys()
.then(keys => {
return Promise.all(
keys
.filter(key => key !== CACHE_STATIC && key !== CACHE_DYNAMIC)
.map(key => caches.delete(key))
);
})
.then(() => self.clients.claim())
);
});
// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
// Cache-first strategy for static assets
if (STATIC_ASSETS.includes(url.pathname)) {
event.respondWith(
caches.match(request)
.then(response => response || fetch(request))
);
return;
}
// Network-first strategy for API calls
if (url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(request)
.then(response => {
// Clone and cache successful responses
if (response.ok) {
const responseClone = response.clone();
caches.open(CACHE_DYNAMIC)
.then(cache => cache.put(request, responseClone));
}
return response;
})
.catch(() => caches.match(request))
);
return;
}
// Stale-while-revalidate for everything else
event.respondWith(
caches.match(request)
.then(cachedResponse => {
const fetchPromise = fetch(request)
.then(networkResponse => {
if (networkResponse.ok) {
const responseClone = networkResponse.clone();
caches.open(CACHE_DYNAMIC)
.then(cache => cache.put(request, responseClone));
}
return networkResponse;
});
return cachedResponse || fetchPromise;
})
);
});<!-- Preconnect to critical third-party origins -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.example.com">
<!-- DNS prefetch for less critical third-parties -->
<link rel="dns-prefetch" href="https://analytics.google.com">
<link rel="dns-prefetch" href="https://www.googletagmanager.com"><!-- Preload LCP image -->
<link
rel="preload"
as="image"
href="/images/hero.webp"
type="image/webp"
imagesrcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w, /images/hero-1200.webp 1200w"
imagesizes="100vw">
<!-- Preload critical CSS -->
<link
rel="preload"
as="style"
href="/styles/critical.css">
<!-- Preload critical JavaScript -->
<link
rel="preload"
as="script"
href="/js/critical.js">
<!-- Preload fonts -->
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/primary.woff2"
crossorigin><!-- Prefetch next likely page -->
<link rel="prefetch" href="/products.html">
<link rel="prefetch" href="/about.html">
<!-- Prefetch resources for next page -->
<link rel="prefetch" as="image" href="/images/products-hero.webp">
<link rel="prefetch" as="script" href="/js/products.js">// Prefetch on hover
document.querySelectorAll('a[href]').forEach(link => {
link.addEventListener('mouseenter', () => {
const url = link.getAttribute('href');
if (url && !url.startsWith('#')) {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = url;
document.head.appendChild(prefetch);
}
}, { once: true });
});
// Prefetch on viewport entry
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const link = entry.target;
const url = link.getAttribute('href');
if (url && !url.startsWith('#')) {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = url;
document.head.appendChild(prefetch);
}
observer.unobserve(link);
}
});
});
document.querySelectorAll('a[href]').forEach(link => {
observer.observe(link);
});<!-- Lightweight facade instead of full embed -->
<div class="youtube-facade" data-video-id="dQw4w9WgXcQ">
<img
src="https://img.youtube.com/vi/dQw4w9WgXcQ/hqdefault.jpg"
alt="Video thumbnail"
loading="lazy">
<button class="play-button" aria-label="Play video"></button>
</div>
<style>
.youtube-facade {
position: relative;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
cursor: pointer;
}
.youtube-facade img {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
}
.play-button {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 68px;
height: 48px;
background: red;
border: none;
border-radius: 8px;
cursor: pointer;
}
</style>
<script>
document.querySelectorAll('.youtube-facade').forEach(facade => {
facade.addEventListener('click', function() {
const videoId = this.dataset.videoId;
const iframe = document.createElement('iframe');
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
iframe.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
iframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:0';
this.innerHTML = '';
this.appendChild(iframe);
}, { once: true });
});
</script><!-- Google Analytics 4 - Async with minimal impact -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
// Load GA script after page load
window.addEventListener('load', () => {
const script = document.createElement('script');
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
script.async = true;
document.head.appendChild(script);
});
</script>
<!-- Or use requestIdleCallback -->
<script>
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
});
}
</script><script>
// Minimal Facebook Pixel implementation
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];
// Load after page load
window.addEventListener('load', () => {
t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s);
});
}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'YOUR_PIXEL_ID');
fbq('track', 'PageView');
</script>// analytics.js
import { onCLS, onFCP, onFID, onLCP, onTTFB, onINP } from 'web-vitals';
function sendToAnalytics(metric) {
// Send to Google Analytics
if (typeof gtag !== 'undefined') {
gtag('event', metric.name, {
event_category: 'Web Vitals',
event_label: metric.id,
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
non_interaction: true,
});
}
// Send to custom analytics endpoint
const data = {
metric: metric.name,
value: metric.value,
id: metric.id,
delta: metric.delta,
rating: metric.rating,
url: window.location.href,
userAgent: navigator.userAgent,
connectionType: navigator.connection?.effectiveType
};
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/vitals', JSON.stringify(data));
} else {
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
keepalive: true
});
}
}
// Monitor all Core Web Vitals
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onTTFB(sendToAnalytics);// performance-monitor.js
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.init();
}
init() {
if ('PerformanceObserver' in window) {
// Observe LCP
this.observeLCP();
// Observe FCP
this.observeFCP();
// Observe CLS
this.observeCLS();
// Observe long tasks
this.observeLongTasks();
}
// Navigation Timing
window.addEventListener('load', () => {
this.captureNavigationTiming();
});
}
observeLCP() {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
this.report('LCP', this.metrics.lcp);
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
}
observeFCP() {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
if (entry.name === 'first-contentful-paint') {
this.metrics.fcp = entry.startTime;
this.report('FCP', this.metrics.fcp);
}
});
});
observer.observe({ entryTypes: ['paint'] });
}
observeCLS() {
let clsValue = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
this.metrics.cls = clsValue;
}
}
this.report('CLS', this.metrics.cls);
});
observer.observe({ entryTypes: ['layout-shift'] });
}
observeLongTasks() {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
this.metrics.longTasks = entries.length;
this.metrics.totalBlockingTime = entries.reduce(
(acc, entry) => acc + Math.max(0, entry.duration - 50),
0
);
this.report('Long Tasks', this.metrics.longTasks);
});
observer.observe({ entryTypes: ['longtask'] });
}
captureNavigationTiming() {
const timing = performance.getEntriesByType('navigation')[0];
if (timing) {
this.metrics.ttfb = timing.responseStart - timing.requestStart;
this.metrics.domLoad = timing.domContentLoadedEventEnd - timing.fetchStart;
this.metrics.windowLoad = timing.loadEventEnd - timing.fetchStart;
this.report('Navigation Timing', {
ttfb: this.metrics.ttfb,
domLoad: this.metrics.domLoad,
windowLoad: this.metrics.windowLoad
});
}
}
report(metricName, value) {
console.log(`${metricName}:`, value);
// Send to analytics
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/metrics', JSON.stringify({
metric: metricName,
value: value,
url: window.location.href,
timestamp: Date.now()
}));
}
}
getMetrics() {
return this.metrics;
}
}
// Initialize
const monitor = new PerformanceMonitor();
window.performanceMonitor = monitor;// webpack.prod.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
publicPath: '/',
clean: true,
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: { ecma: 8 },
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
drop_console: true,
pure_funcs: ['console.log', 'console.info'],
},
mangle: { safari10: true },
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
parallel: true,
}),
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all',
maxInitialRequests: 25,
minSize: 20000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
name(module) {
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1];
return `npm.${packageName.replace('@', '')}`;
},
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
runtimeChunk: 'single',
moduleIds: 'deterministic',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].chunk.css',
}),
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
compressionOptions: { level: 11 },
threshold: 10240,
minRatio: 0.8,
}),
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
// Analyze bundle only when needed
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean),
performance: {
maxAssetSize: 250000,
maxEntrypointSize: 250000,
hints: 'warning',
},
};// next.config.js
module.exports = {
// Enable SWC minification
swcMinify: true,
// Compress output
compress: true,
// Image optimization
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 31536000,
},
// Headers
async headers() {
return [
{
source: '/:all*(svg|jpg|jpeg|png|webp|avif|gif|ico)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
{
source: '/_next/static/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
];
},
// Webpack customization
webpack: (config, { dev, isServer }) => {
// Production optimizations
if (!dev && !isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2,
},
react: {
name: 'react',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
},
},
};
}
return config;
},
};# .lighthouserc.json
{
"ci": {
"collect": {
"numberOfRuns": 3,
"startServerCommand": "npm run serve",
"url": [
"http://localhost:3000",
"http://localhost:3000/about",
"http://localhost:3000/products"
],
"settings": {
"preset": "desktop",
"throttling": {
"rttMs": 40,
"throughputKbps": 10240,
"cpuSlowdownMultiplier": 1
}
}
},
"assert": {
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}],
"categories:best-practices": ["error", {"minScore": 0.9}],
"categories:seo": ["error", {"minScore": 0.9}],
"first-contentful-paint": ["error", {"maxNumericValue": 1800}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 200}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}# .github/workflows/performance.yml
name: Performance Check
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run Lighthouse CI
run: |
npm install -g @lhci/[email protected]
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: lighthouse-results
path: .lighthouseci{
"performance": {
"budgets": [
{
"resourceSizes": [
{
"resourceType": "script",
"budget": 300
},
{
"resourceType": "stylesheet",
"budget": 50
},
{
"resourceType": "image",
"budget": 500
},
{
"resourceType": "font",
"budget": 100
},
{
"resourceType": "total",
"budget": 1000
}
],
"resourceCounts": [
{
"resourceType": "script",
"budget": 10
},
{
"resourceType": "stylesheet",
"budget": 3
},
{
"resourceType": "third-party",
"budget": 10
}
]
}
]
}
}- Enable compression (Brotli/gzip)
- Set up proper caching headers
- Add image dimensions
- Enable native lazy loading
- Minify CSS/JS
- Convert images to WebP/AVIF
- Implement responsive images
- Extract and inline critical CSS
- Set up code splitting
- Optimize fonts
- Implement service worker
- Add resource hints
- Optimize third-party scripts
- Set up performance monitoring
- Set up Lighthouse CI
- Configure performance budgets
- Implement Web Vitals tracking
- Document optimizations
- Train team on best practices
Document Version: 1.0
Companion to: PageSpeed Universal Optimization Checklist
Last Updated: November 2025
β
LCP (Largest Contentful Paint) β€ 2.5s
β
CLS (Cumulative Layout Shift) β€ 0.1
β
INP (Interaction to Next Paint) β€ 200ms
β
FCP (First Contentful Paint) β€ 1.8s
β
TTFB (Time to First Byte) β€ 800ms
# Nginx
brotli on;
brotli_comp_level 6;
gzip on;
gzip_comp_level 6;<!-- ALWAYS include width and height -->
<img src="image.jpg" width="800" height="600" alt="Description"><!-- Above fold -->
<img src="hero.jpg" loading="eager" fetchpriority="high">
<!-- Below fold -->
<img src="content.jpg" loading="lazy"><picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Fallback">
</picture><script src="app.js" defer></script>
<script src="analytics.js" async></script><head>
<style>/* Critical CSS here */</style>
<link rel="preload" href="full.css" as="style" onload="this.rel='stylesheet'">
</head><link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: swap;
}
</style># Static assets - 1 year
location ~* \.(css|js|jpg|png|webp|woff2)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# HTML - no cache
location ~* \.html$ {
add_header Cache-Control "no-cache";
}<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">npm install -D terser cssnano html-minifier
# Add to build process# Convert to WebP
cwebp -q 85 input.jpg -o output.webp
# Convert to AVIF
avifenc --min 0 --max 63 input.jpg output.avif
# Optimize JPEG
jpegoptim --max=85 --strip-all *.jpg
# Optimize PNG
pngquant --quality=65-80 *.png
# Batch optimize with Sharp (Node.js)
npm install sharp
node -e "const sharp=require('sharp'); sharp('input.jpg').webp({quality:85}).toFile('output.webp');"<picture>
<!-- AVIF -->
<source
type="image/avif"
srcset="img-400.avif 400w, img-800.avif 800w, img-1200.avif 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px">
<!-- WebP -->
<source
type="image/webp"
srcset="img-400.webp 400w, img-800.webp 800w, img-1200.webp 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px">
<!-- Fallback -->
<img
src="img-800.jpg"
srcset="img-400.jpg 400w, img-800.jpg 800w, img-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
width="1200"
height="675"
alt="Description"
loading="lazy">
</picture># Install critical
npm install -g critical
# Extract critical CSS
critical index.html --base dist/ --inline > index-critical.html
# Or use in build script
npx critical index.html --base dist/ --inlinemodule.exports = {
mode: 'production',
optimization: {
minimize: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
};<!-- Preload critical fonts -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="/fonts/primary.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'Primary';
src: url('/fonts/primary.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap; /* or 'optional' for better CLS */
}
</style>
<!-- Self-hosted Google Fonts alternative -->
<!-- Use: https://google-webfonts-helper.herokuapp.com/ --><head>
<!-- DNS Prefetch for third-parties -->
<link rel="dns-prefetch" href="https://analytics.google.com">
<!-- Preconnect for critical third-parties -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
<!-- Preload LCP image -->
<link rel="preload" as="image" href="hero.webp">
<!-- Preload critical CSS/JS -->
<link rel="preload" as="style" href="critical.css">
<link rel="preload" as="script" href="app.js">
<!-- Preload critical font -->
<link rel="preload" as="font" type="font/woff2" href="font.woff2" crossorigin>
<!-- Prefetch next likely navigation -->
<link rel="prefetch" href="/about.html">
</head>// sw.js
const CACHE = 'v1';
const ASSETS = ['/','/app.js','/style.css','/font.woff2'];
self.addEventListener('install', e => {
e.waitUntil(caches.open(CACHE).then(cache => cache.addAll(ASSETS)));
});
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(res => res || fetch(e.request))
);
});
// Register in app
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}<div class="yt" data-id="VIDEO_ID" onclick="
const iframe=document.createElement('iframe');
iframe.src='https://www.youtube.com/embed/'+this.dataset.id+'?autoplay=1';
iframe.allow='autoplay;encrypted-media';
iframe.allowFullscreen=true;
iframe.style='position:absolute;top:0;left:0;width:100%;height:100%';
this.innerHTML='';
this.appendChild(iframe);
" style="position:relative;padding-bottom:56.25%;background:url(https://img.youtube.com/vi/VIDEO_ID/hqdefault.jpg) center/cover;cursor:pointer">
<button style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:68px;height:48px;background:red;border:0;border-radius:8px"></button>
</div>// Install: npm install web-vitals
import {onCLS, onFCP, onLCP, onTTFB, onINP} from 'web-vitals';
function sendToAnalytics({name, value, id}) {
gtag('event', name, {
event_category: 'Web Vitals',
value: Math.round(name === 'CLS' ? value * 1000 : value),
event_label: id,
non_interaction: true,
});
}
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onLCP(sendToAnalytics);
onTTFB(sendToAnalytics);
onINP(sendToAnalytics);# Lighthouse CLI
npm install -g lighthouse
lighthouse https://your-site.com --view
# With specific settings
lighthouse https://your-site.com --preset=desktop --output=html --output-path=./report.html
# PageSpeed Insights API
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://your-site.com&strategy=mobile"
# Chrome DevTools Performance
# Open DevTools > Performance > Record > Stop > Analyze
# WebPageTest CLI
npm install -g webpagetest
webpagetest test https://your-site.comβ
Optimize server response (TTFB < 800ms)
β
Eliminate render-blocking resources
β
Use CDN for assets
β
Preload LCP image
β
Use appropriate image format (AVIF/WebP)
β
Compress images (<200KB for hero)
β
Set fetchpriority="high" on LCP image
β
Avoid lazy loading LCP image
β
Use HTTP/2 or HTTP/3
β
Enable compression (Brotli)
β
Set width/height on all images
β
Set dimensions for ads/embeds
β
Reserve space for dynamic content
β
Use font-display: swap/optional
β
Preload critical fonts
β
Avoid inserting content above viewport
β
Use transform instead of layout properties
β
Set aspect-ratio for responsive images
β
Avoid animations of top/left/width/height
β
Test with slow network (layout shifts often appear then)
β
Break up long tasks (>50ms)
β
Minimize JavaScript execution time
β
Defer non-critical JavaScript
β
Use code splitting
β
Implement debounce/throttle for handlers
β
Use passive event listeners
β
Minimize third-party scripts
β
Use web workers for heavy computation
β
Reduce DOM complexity
β
Optimize rendering work
Fix:
- Optimize hero image (WebP/AVIF, compression)
- Preload LCP element
- Reduce server response time
- Eliminate render-blocking resources
Fix:
- Add explicit dimensions to images
- Reserve space for ads/dynamic content
- Preload fonts with font-display: swap
Fix:
- Break up long JavaScript tasks
- Reduce third-party script impact
- Optimize event handlers
- Use requestIdleCallback for non-urgent work
Fix:
<!-- Inline critical CSS -->
<style>/* critical CSS */</style>
<!-- Defer JavaScript -->
<script src="app.js" defer></script>
<!-- Async load CSS -->
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'">JavaScript: < 300KB (gzipped)
CSS: < 50KB (gzipped)
Images (total): < 500KB
Fonts: < 100KB
Total page size: < 1MB
LCP: < 2.5s
FCP: < 1.8s
CLS: < 0.1
INP: < 200ms
TTFB: < 800ms
<!-- Title (50-60 chars) -->
<title>Page Title | Site Name</title>
<!-- Meta Description (150-160 chars) -->
<meta name="description" content="Compelling description with keywords">
<!-- Viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Open Graph -->
<meta property="og:title" content="Title">
<meta property="og:description" content="Description">
<meta property="og:image" content="https://site.com/image.jpg">
<!-- Canonical -->
<link rel="canonical" href="https://site.com/page">
<!-- Schema.org (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Article Title",
"image": "image.jpg",
"datePublished": "2025-01-01"
}
</script><!-- Images -->
<img src="image.jpg" alt="Descriptive text" width="800" height="600">
<!-- Links -->
<a href="/page">Descriptive link text (not "click here")</a>
<!-- Buttons -->
<button aria-label="Close dialog">Γ</button>
<!-- Headings (proper hierarchy) -->
<h1>Main Heading</h1>
<h2>Subheading</h2>
<!-- Form Labels -->
<label for="email">Email Address</label>
<input type="email" id="email" name="email">
<!-- Language -->
<html lang="en">
<!-- Focus visible -->
button:focus-visible {
outline: 2px solid blue;
}<!-- Viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Touch Icons -->
<link rel="apple-touch-icon" href="icon-180.png">
<meta name="theme-color" content="#000000">
<!-- Disable zoom on input focus (use carefully) -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<!-- Optimize touch targets (48x48px minimum) -->
<style>
button, a {
min-height: 48px;
min-width: 48px;
}
</style># Nginx
# HTML - no cache
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Versioned assets - 1 year cache
location ~* \.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Images - 1 year cache
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Fonts - 1 year cache + CORS
location ~* \.(woff|woff2|ttf|eot)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin "*";
}
# API - short cache with revalidation
location /api/ {
add_header Cache-Control "private, max-age=300, stale-while-revalidate=60";
}// Basic RUM setup
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Send to analytics
if (navigator.sendBeacon) {
navigator.sendBeacon('/analytics', JSON.stringify({
metric: entry.name,
value: entry.value || entry.startTime,
url: location.href
}));
}
}
});
observer.observe({ entryTypes: ['paint', 'largest-contentful-paint', 'layout-shift'] });Before pushing to production:
- All images have width/height attributes
- LCP image is optimized and preloaded
- Critical CSS is inlined
- JavaScript is minified and split
- Compression is enabled (Brotli/gzip)
- Cache headers are set correctly
- Fonts are preloaded with font-display: swap
- Third-party scripts load asynchronously
- Service worker is implemented (if applicable)
- Lighthouse score is 90+ across all categories
- Core Web Vitals are passing
- Performance budget is not exceeded
- Testing done on real devices
- Monitoring/analytics is set up
Priority devices for testing:
- iPhone 13/14 (iOS Safari)
- Samsung Galaxy S21/S22 (Chrome Android)
- iPad (Safari)
- Desktop 1920Γ1080 (Chrome)
- Desktop 1366Γ768 (Edge)
Network conditions to test:
- Fast 3G (750 Kbps)
- Slow 4G (4 Mbps)
- WiFi (20+ Mbps)
Testing:
- PageSpeed Insights: https://pagespeed.web.dev/
- Lighthouse: https://developer.chrome.com/docs/lighthouse/
- WebPageTest: https://www.webpagetest.org/
- Chrome DevTools: Built into Chrome
Image Optimization:
- Squoosh: https://squoosh.app/
- ImageOptim: https://imageoptim.com/
- Sharp: https://sharp.pixelplumbing.com/
Monitoring:
- Google Search Console: https://search.google.com/search-console
- Chrome UX Report: https://developer.chrome.com/docs/crux/
- web-vitals library: https://github.com/GoogleChrome/web-vitals
Analysis:
- Webpack Bundle Analyzer: webpack plugin
- Bundlephobia: https://bundlephobia.com/
- Prioritize mobile first - Mobile performance is harder, optimize there first
- Test on real devices - Emulators don't show real performance
- Focus on Core Web Vitals - They have the most impact on UX and SEO
- Set performance budgets - Prevent regressions with automated checks
- Monitor continuously - Performance degrades over time, monitor it
- Optimize images first - Usually the biggest wins for least effort
- Lazy load everything below fold - Except critical content
- Inline critical CSS only - Keep it under 14KB
- Self-host fonts - Better caching, fewer connections
- Test with slow networks - Many users have slow connections
Version: 1.0
Print this and keep it handy!
Last Updated: November 2025
LinkedIn // GitHub // Medium // Twitter/X
A bit about David Youngblood...
David is a Partner, Father, Student, and Teacher, embodying the essence of a true polyoptic polymath and problem solver. As a Generative AI Prompt Engineer, Language Programmer, Context-Architect, and Artist, David seamlessly integrates technology, creativity, and strategic thinking to co-create systems of enablement and allowance that enhance experiences for everyone.
As a serial autodidact, David thrives on continuous learning and intellectual growth, constantly expanding his knowledge across diverse fields. His multifaceted career spans technology, sales, and the creative arts, showcasing his adaptability and relentless pursuit of excellence. At LouminAI Labs, David leads research initiatives that bridge the gap between advanced AI technologies and practical, impactful applications.
David's philosophy is rooted in thoughtful introspection and practical advice, guiding individuals to navigate the complexities of the digital age with self-awareness and intentionality. He passionately advocates for filtering out digital noise to focus on meaningful relationships, personal growth, and principled living. His work reflects a deep commitment to balance, resilience, and continuous improvement, inspiring others to live purposefully and authentically.
David believes in the power of collaboration and principled responsibility in leveraging AI for the greater good. He challenges the status quo, inspired by the spirit of the "crazy ones" who push humanity forward. His commitment to meritocracy, excellence, and intelligence drives his approach to both personal and professional endeavors.
"Hereβs to the crazy ones, the misfits, the rebels, the troublemakers, the round pegs in the square holesβ¦ the ones who see things differently; theyβre not fond of rules, and they have no respect for the status quoβ¦ They push the human race forward, and while some may see them as the crazy ones, we see genius, because the people who are crazy enough to think that they can change the world, are the ones who do." β Apple, 1997
Why I Exist? To experience life in every way, at every moment. To "BE".
What I Love to Do While Existing? Co-creating here, in our collective, combined, and interoperably shared experience.
How Do I Choose to Experience My Existence? I choose to do what I love. I love to co-create systems of enablement and allowance that help enhance anyone's experience.
Who Do I Love Creating for and With? Everyone of YOU! I seek to observe and appreciate the creativity and experiences made by, for, and from each of us.
When & Where Does All of This Take Place? Everywhere, in every moment, of every day. It's a very fulfilling place to be... I'm learning to be better about observing it as it occurs.
I've learned a few overarching principles that now govern most of my day-to-day decision-making when it comes to how I choose to invest my time and who I choose to share it with:
- Work/Life/Sleep (Health) Balance: Family first; does your schedule agree?
- Love What You Do, and Do What You Love: If you have what you hold, what are YOU holding on to?
- Response Over Reaction: Take pause and choose how to respond from the center, rather than simply react from habit, instinct, or emotion.
- Progress Over Perfection: One of the greatest inhibitors of growth.
- Inspired by "7 Habits of Highly Effective People": Integrating Coveyβs principles into daily life.
David is dedicated to fostering meaningful connections and intentional living, leveraging his diverse skill set to make a positive impact in the world. Whether through his technical expertise, creative artistry, or philosophical insights, he strives to empower others to live their best lives by focusing on what truly matters.
β David Youngblood