Deploying Progressive Web Apps
Deploying a PWA requires specific considerations to ensure all features work correctly in production. This lesson covers hosting requirements, deployment strategies, and best practices for various platforms.
PWA Hosting Requirements
PWAs have specific hosting requirements that must be met for proper functionality.
Critical Requirements:
- HTTPS: Service workers require secure contexts (HTTPS), except for localhost during development
- Proper MIME Types: Service worker files must be served with
application/javascript or text/javascript
- Service-Worker-Allowed Header: Controls the scope of the service worker
- Cache Headers: Configure proper cache control for service worker files
- Manifest MIME Type: manifest.json must be served with
application/manifest+json
Deploying to Netlify
Netlify is one of the easiest platforms for deploying PWAs with automatic HTTPS and global CDN.
# Install Netlify CLI
npm install -g netlify-cli
# Build your PWA
npm run build
# Deploy to Netlify
netlify deploy --prod --dir=build
# Or connect to Git for automatic deployments
netlify init
# netlify.toml - Configuration file
[build]
publish = "build"
command = "npm run build"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/service-worker.js"
[headers.values]
Cache-Control = "public, max-age=0, must-revalidate"
[[headers]]
for = "/manifest.json"
[headers.values]
Content-Type = "application/manifest+json"
Cache-Control = "public, max-age=0, must-revalidate"
[[headers]]
for = "/static/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Pro Tip: Netlify automatically provides HTTPS, HTTP/2, and global CDN without additional configuration. Perfect for PWAs!
Deploying to Vercel
Vercel offers excellent support for PWAs with zero-config deployments and automatic HTTPS.
# Install Vercel CLI
npm install -g vercel
# Build and deploy
vercel --prod
# Or use Git integration
# Push to GitHub and connect repository in Vercel dashboard
// vercel.json - Configuration file
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "build"
}
}
],
"routes": [
{
"src": "/service-worker.js",
"headers": {
"cache-control": "public, max-age=0, must-revalidate"
},
"dest": "/service-worker.js"
},
{
"src": "/manifest.json",
"headers": {
"content-type": "application/manifest+json",
"cache-control": "public, max-age=0, must-revalidate"
},
"dest": "/manifest.json"
},
{
"src": "/static/(.*)",
"headers": {
"cache-control": "public, max-age=31536000, immutable"
},
"dest": "/static/$1"
},
{
"src": "/(.*)",
"dest": "/index.html"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
]
}
Deploying to Firebase Hosting
Firebase Hosting provides fast and secure hosting with global CDN and automatic SSL certificates.
# Install Firebase CLI
npm install -g firebase-tools
# Login to Firebase
firebase login
# Initialize Firebase project
firebase init hosting
# Select options:
# - What do you want to use as your public directory? build
# - Configure as single-page app? Yes
# - Set up automatic builds with GitHub? (Optional) Yes
# Build your app
npm run build
# Deploy to Firebase
firebase deploy --only hosting
// firebase.json - Configuration file
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/service-worker.js",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=0, must-revalidate"
}
]
},
{
"source": "/manifest.json",
"headers": [
{
"key": "Content-Type",
"value": "application/manifest+json"
},
{
"key": "Cache-Control",
"value": "public, max-age=0, must-revalidate"
}
]
},
{
"source": "**/*.@(jpg|jpeg|gif|png|svg|webp)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "**/*.@(js|css)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "**",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
]
}
}
HTTPS Setup for Custom Domains
If you're hosting on your own server, you need to set up HTTPS using Let's Encrypt.
# Install Certbot (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
# Obtain SSL certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Automatic renewal is set up by default
# Test renewal process
sudo certbot renew --dry-run
# Nginx configuration for PWA
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
root /var/www/html;
index index.html;
# Service worker
location /service-worker.js {
add_header Cache-Control "public, max-age=0, must-revalidate";
add_header Content-Type "application/javascript";
}
# Manifest
location /manifest.json {
add_header Content-Type "application/manifest+json";
add_header Cache-Control "public, max-age=0, must-revalidate";
}
# Static assets
location /static/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
Service Worker Cache Invalidation
When deploying updates, you need to ensure users get the new version without manual cache clearing.
// Versioning strategy in service worker
const CACHE_VERSION = 'v2.0.1'; // Update this with each deployment
const CACHE_NAME = `pwa-cache-${CACHE_VERSION}`;
// Delete old caches on activation
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name.startsWith('pwa-cache-'))
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
});
Important: Never cache the service worker file itself! Always serve it with Cache-Control: no-cache or max-age=0 to ensure users get updates immediately.
CDN Configuration for PWAs
Configure your CDN to properly handle PWA files with appropriate caching strategies.
# Cloudflare Page Rules Example
# Rule 1: Service Worker (No Cache)
URL: *yourdomain.com/service-worker.js
Cache Level: Bypass
Browser Cache TTL: Respect Existing Headers
# Rule 2: Manifest (No Cache)
URL: *yourdomain.com/manifest.json
Cache Level: Bypass
Browser Cache TTL: Respect Existing Headers
# Rule 3: Static Assets (Cache Everything)
URL: *yourdomain.com/static/*
Cache Level: Cache Everything
Edge Cache TTL: 1 year
Browser Cache TTL: 1 year
# Rule 4: HTML Files (No Cache)
URL: *yourdomain.com/*.html
Cache Level: Bypass
Browser Cache TTL: Respect Existing Headers
Deployment Checklist
Pre-Deployment Checklist:
- ✓ Test PWA in production build locally
- ✓ Verify all icons are present and correct sizes
- ✓ Test offline functionality
- ✓ Check service worker registration
- ✓ Validate manifest.json syntax
- ✓ Test on mobile devices (Android/iOS)
- ✓ Run Lighthouse audit (aim for 90+ PWA score)
- ✓ Verify HTTPS is enabled
- ✓ Test install prompt
- ✓ Check caching strategies work correctly
- ✓ Verify update mechanism works
- ✓ Test cross-browser compatibility
Exercise:
- Build your PWA for production
- Deploy to Netlify using the CLI
- Configure netlify.toml with proper headers
- Test PWA installation on mobile device
- Run Lighthouse audit and fix any issues
- Deploy to Firebase Hosting as alternative
- Set up custom domain with HTTPS
- Implement cache versioning strategy
- Test service worker updates in production
- Configure CDN rules for optimal performance
Monitoring Tip: Use Google Analytics or similar tools to track PWA installations, offline usage, and service worker errors in production. Add custom events for "beforeinstallprompt" and "appinstalled" to measure install conversions.