SSG Deployment — Deploying Static Sites to CDNs and Hosting
In this tutorial, you will learn about SSG Deployment. We cover key concepts, practical examples, and best practices to help you master this topic.
SSG deployment involves building static files, deploying to CDN hosting, configuring custom domains, and setting up CI/CD for automatic rebuilds.
What You'll Learn
By the end of this tutorial, you'll understand how to deploy SSG sites to Netlify, Vercel, AWS S3, and Cloudflare Pages. You'll learn CDN configuration, custom domains, SSL, and CI/CD pipeline setup.
Why It Matters
SSG sites are static files that can be served from anywhere. Choosing the right deployment platform affects performance, cost, and developer experience. Proper deployment ensures global availability and fast load times.
Real-World Use
A company blog built with Hugo deploys to Netlify. On every git push, Netlify builds the site, deploys to its global CDN, and invalidates the cache. The site loads in under 200ms worldwide.
Deployment Architecture
graph LR
A[Git Repository] --> B[CI/CD Pipeline]
B --> C[Build SSG
npm run build]
C --> D[Static files
public/ directory]
D --> E[Deploy to CDN]
E --> F[Netlify / Vercel / S3 / CF Pages]
F --> G[Global CDN Edge]
F --> H[Custom domain
example.com]
F --> I[SSL Certificate
Auto-provisioned]
G --> J[User request
served from edge]
style B fill:#4a90d9,color:#fff
style C fill:#e67e22,color:#fff
style F fill:#27ae60,color:#fff
Netlify Deployment
# netlify.toml — Netlify configuration
[build]
command = "npm run build"
publish = "public"
[build.environment]
NODE_VERSION = "18"
# Redirect and header rules
[[redirects]]
from = "/blog/*"
to = "/blog/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/404.html"
status = 404
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.html"
[headers.values]
Cache-Control = "public, max-age=0, must-revalidate"
Vercel Deployment
// vercel.json — Vercel configuration
{
"buildCommand": "npm run build",
"outputDirectory": "public",
"framework": "nextjs",
"regions": ["all"],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
}
]
},
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
],
"crons": [
{
"path": "/api/revalidate",
"schedule": "0 */6 * * *"
}
]
}
AWS S3 + CloudFront
// scripts/deploy-s3.js — Deploy to S3 + CloudFront
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { CloudFrontClient, CreateInvalidationCommand } = require('@aws-sdk/client-cloudfront');
const fs = require('fs');
const path = require('path');
const mime = require('mime-types');
const s3 = new S3Client({ region: process.env.AWS_REGION });
const cloudfront = new CloudFrontClient({ region: process.env.AWS_REGION });
const BUCKET = process.env.S3_BUCKET;
const DISTRIBUTION_ID = process.env.CLOUDFRONT_DISTRIBUTION_ID;
const BUILD_DIR = './public';
async function uploadFile(filePath, relativePath) {
const content = fs.readFileSync(filePath);
const contentType = mime.lookup(filePath) || 'application/octet-stream';
const params = {
Bucket: BUCKET,
Key: relativePath,
Body: content,
ContentType: contentType,
CacheControl: filePath.match(/\.(css|js|png|jpg|webp|avif)$/)
? 'public, max-age=31536000, immutable'
: 'public, max-age=0, must-revalidate',
};
await s3.send(new PutObjectCommand(params));
console.log(`Uploaded: ${relativePath}`);
}
async function uploadDirectory(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const relativePath = path.relative(BUILD_DIR, fullPath);
if (entry.isDirectory()) {
await uploadDirectory(fullPath);
} else {
await uploadFile(fullPath, relativePath);
}
}
}
async function invalidateCache() {
const params = {
DistributionId: DISTRIBUTION_ID,
InvalidationBatch: {
CallerReference: `invalidate-${Date.now()}`,
Paths: {
Quantity: 1,
Items: ['/*'],
},
},
};
await cloudfront.send(new CreateInvalidationCommand(params));
console.log('CloudFront cache invalidated');
}
async function deploy() {
console.log('Uploading to S3...');
await uploadDirectory(BUILD_DIR);
console.log('Invalidating CloudFront cache...');
await invalidateCache();
console.log('Deploy complete!');
}
deploy().catch(console.error);
Cloudflare Pages
# wrangler.toml — Cloudflare Pages configuration
name = "my-static-site"
compatibility_date = "2026-06-28"
[build]
command = "npm run build"
publish = "public"
[build.upload]
format = "service-worker"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
Common Mistakes
- Not configuring caching headers. Without explicit Cache-Control, CDNs may not cache assets effectively. Set immutable cache for fingerprinted assets.
- Missing 404 page configuration. SSG sites need a custom 404.html. Redirect unknown paths to it via platform redirect rules.
- Deploying without CI/CD. Manual deployment is error-prone. Set up automatic builds on git push using Netlify, Vercel, or GitHub Actions.
- Not handling SPA-style routing. If using client-side routing, configure all paths to serve index.html. Use redirect rules on the hosting platform.
- Forgetting SSL certificate provisioning. Always serve over HTTPS. Most platforms auto-provision SSL. For custom setups, use Let's Encrypt.
Practice Questions
- What are the key differences between Netlify, Vercel, and S3+CloudFront for SSG deployment?
- How do you configure custom domains and SSL for a static site?
- What caching headers should you set for static assets vs HTML pages?
- How do you handle 404 errors on a statically deployed site?
- What CI/CD triggers should rebuild and redeploy an SSG site?
Challenge: Deploy an SSG site to three platforms: configure Netlify with redirect rules and headers, deploy the same site to Vercel, and set up S3+CloudFront with automated deployment script. Compare the setup complexity and performance.
FAQ
Mini Project
Set up a complete SSG deployment pipeline: build a site with Hugo or Next.js, configure Netlify with netlify.toml (redirects, headers), set up automatic builds from a git Repository, add a custom domain with SSL, and verify the deployment.
What's Next
Your site is deployed. Now add SSG Analytics to track visitors and measure site performance.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro