SSR vs SSG Performance — Server-Side Rendering vs Static Generation
In this tutorial, you will learn how server-side rendering and static site generation affect performance, when to use each approach, and how to optimize both. SSR generates HTML on each request while SSG pre-builds HTML at deploy time. DodaTech uses SSG for documentation and SSR for the interactive DodaZIP dashboard, choosing each based on content characteristics.
What You Will Learn
- How SSR and SSG differ in build time, runtime, and caching behavior
- How to measure and optimize SSR response times
- How to measure and optimize SSG page weight
- How hybrid rendering combines the best of both approaches
Why It Matters
The rendering approach directly determines TTFB, FCP, and the overall user experience. Choosing the wrong strategy can add 2-3 seconds of latency. DodaTech migrated its documentation site from SSR to SSG and saw TTFB drop from 800ms to 50ms because pages were served directly from CDN edge without any server processing.
Real-World Use Case
DodaTech originally built its documentation site with SSR using Next.js. As the site grew to 2000 pages, build times became irrelevant for the static approach, but server costs for SSR remained high. The migration to SSG (Next.js static export + Cloudflare Pages) reduced cloud costs by 60 percent and median page load time by 75 percent.
Prerequisites
You should understand HTTP request-response flow and basic React or Next.js concepts. Familiarity with CDN Configuration is helpful.
Step-by-Step Tutorial
Step 1: Understand the Performance Trade-offs
| Aspect | SSR | SSG |
|---|---|---|
| TTFB | Higher (server processing) | Very low (static file) |
| FCP | Good (full HTML) | Good (full HTML) |
| Caching | Dynamic (limited) | Aggressive (full page) |
| Cold start | Server processing time | None |
| Build time | None | Build time per deploy |
| Content freshness | Always fresh | Requires rebuild |
SSR is best for personalized, dynamic content. SSG is best for public, infrequently changing content.
Step 2: Measure SSR Performance
SSR adds server processing time to TTFB. Profile the server-side rendering function to identify bottlenecks.
// Next.js SSR page with timing instrumentation
export async function getServerSideProps({ req, res }) {
const start = Date.now();
// Simulate data fetching
const data = await fetch('https://api.dodatech.com/products').then(r => r.json());
const duration = Date.now() - start;
console.log(`SSR render time: ${duration}ms`);
// Add timing header for monitoring
res.setHeader('Server-Timing', `ssr;dur=${duration}`);
return { props: { data } };
}
Expected output: The Server-Timing header ssr;dur=340 indicates 340ms of server processing time. If this exceeds 500ms, optimize the data fetching or caching.
Step 3: Optimize SSR with Incremental Rendering
Use streaming SSR and component-level caching to reduce server processing time.
// React 18 streaming SSR with Suspense boundaries
import { Suspense } from 'react';
function ProductPage({ productId }) {
return (
<div>
<h1>Product Details</h1>
<Suspense fallback={<div>Loading reviews...</div>}>
<Reviews productId={productId} />
</Suspense>
</div>
);
}
async function Reviews({ productId }) {
const reviews = await fetch(`https://api.dodatech.com/reviews/${productId}`)
.then(r => r.json());
return reviewList(reviews);
}
Expected behavior: The page renders the product details immediately and streams the reviews section when ready. The user sees content faster even if the reviews query is slow.
Step 4: Measure SSG Performance
SSG pages are pre-built HTML files served directly from a CDN. Measure their performance with Lighthouse.
# Lighthouse audit for SSG page
npx lighthouse https://docs.dodatech.com/getting-started --output json | \
python3 -c "import sys,json;d=json.load(sys.stdin);print(f\"FCP: {d['audits']['first-contentful-paint']['numericValue']}ms, LCP: {d['audits']['largest-contentful-paint']['numericValue']}ms\")"
Expected output: FCP: 320ms, LCP: 450ms. SSG pages typically have FCP under 500ms because the HTML is served immediately.
Step 5: Optimize SSG with Incremental Static Regeneration (ISR)
ISR updates static pages without a full rebuild by revalidating them in the background.
// Next.js ISR page
export async function getStaticProps() {
const data = await fetch('https://api.dodatech.com/products').then(r => r.json());
return {
props: { data },
revalidate: 300, // Revalidate every 5 minutes
};
}
export async function getStaticPaths() {
const products = await fetch('https://api.dodatech.com/products/ids').then(r => r.json());
return {
paths: products.map(p => ({ params: { id: p.id } })),
fallback: 'blocking', // Generate on-demand for new content
};
}
Expected behavior: The first visitor after a rebuild hits a static page. If the page is older than 5 minutes, the next visitor triggers a background regeneration while still seeing the stale page. Subsequent visitors see the fresh page.
Step 6: Implement Hybrid Rendering
Use SSR for dynamic routes and SSG for static routes within the same application.
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/docs/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, s-maxage=31536000, immutable' }
]
},
{
source: '/dashboard/:path*',
headers: [
{ key: 'Cache-Control', value: 'private, no-cache' }
]
}
];
}
};
Documentation pages (SSG) get aggressive caching. Dashboard pages (SSR) bypass cache entirely.
Step 7: Compare Cold Start vs Warm Start
SSR pages have different performance on first request (cold start) vs subsequent requests. Measure both.
# Measure cold SSR (first request after deployment)
curl -s -o /dev/null -w "Cold: %{time_starttransfer}s\n" https://app.dodatech.com/
# Measure warm SSR (subsequent request)
curl -s -o /dev/null -w "Warm: %{time_starttransfer}s\n" https://app.dodatech.com/
Expected output: Cold: 1.2s, Warm: 0.4s. The cold start is slower because the server needs to initialize the application.
Step 8: Make the Decision Matrix
Use this decision matrix to choose between SSR and SSG:
| Content Type | SSR | SSG | ISR |
|---|---|---|---|
| Marketing pages | No | Yes | No |
| Product listing (public) | No | Yes | Yes |
| Product details (personalized) | Yes | No | No |
| User dashboard | Yes | No | No |
| Blog posts | No | Yes | Yes |
| Documentation | No | Yes | No |
function chooseRenderingStrategy(pageType, contentFrequency, personalization) {
if (personalization) return 'SSR';
if (contentFrequency === 'realtime') return 'SSR';
if (contentFrequency === 'hourly') return 'ISR';
return 'SSG';
}
Learning Path
flowchart LR A[Critical Rendering Path] --> B[SSR vs SSG Performance] B --> C[Cache Strategy] B --> D[Database Query Optimization] C --> E[Performance Testing] style B fill:#4f46e5,color:#fff style A fill:#6366f1,color:#fff style C fill:#6366f1,color:#fff
Common Errors
Using SSR for public content: Public content that could be SSG wastes server resources and adds latency. If the content is the same for every user, it should be static.
No caching on SSR pages: Even SSR pages should have CDN-level caching for anonymous users. Implement cache headers that match the staleness tolerance of the content.
Rebuilding the entire site on every content change: With ISR, only the changed page needs regeneration. Full rebuilds are wasteful for frequently updated sites.
Using fallback: true for ISR without a loading state: The
fallback: truemode shows an empty page until the static generation completes. Usefallback: 'blocking'for SEO-critical pages.Measuring SSG performance without CDN: Testing SSG directly from the origin server misses the CDN caching benefit. Always test through the CDN URL.
Not accounting for database connection warmup: SSR pages that connect to a database experience slow first requests due to Connection Pool warmup. Use connection pooling with pre-warming.
Practice Questions
- What is the main performance advantage of SSG over SSR?
- How does Incremental Static Regeneration (ISR) differ from full SSG?
- Why might SSR have different cold start vs warm start performance?
- What is streaming SSR and how does it improve perceived performance?
- When would you choose SSR over SSG despite SSG being faster?
Answers: 1. SSG serves pre-built HTML files directly from CDN edge with no server processing, resulting in much lower TTFB. 2. ISR regenerates individual pages in the background when they become stale, without requiring a full site rebuild. 3. Cold start requires application initialization, database connection establishment, and JIT Compilation that are cached for warm requests. 4. Streaming SSR sends HTML to the browser in chunks as soon as each part is ready, allowing the browser to render content progressively. 5. When content is personalized per user (dashboards, account pages), SSR is required because the content cannot be pre-built.
Challenge
Build a hybrid Next.js application with three page types: a documentation page using SSG with ISR (revalidate every hour), a product listing page using ISR with fallback blocking, and a user dashboard using SSR. Implement appropriate Cache-Control headers for each page type and measure the TTFB, FCP, and LCP difference between the three rendering strategies.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro