Progressive Web Apps (PWA)

Deploying PWAs

20 min Lesson 24 of 30

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:
  1. ✓ Test PWA in production build locally
  2. ✓ Verify all icons are present and correct sizes
  3. ✓ Test offline functionality
  4. ✓ Check service worker registration
  5. ✓ Validate manifest.json syntax
  6. ✓ Test on mobile devices (Android/iOS)
  7. ✓ Run Lighthouse audit (aim for 90+ PWA score)
  8. ✓ Verify HTTPS is enabled
  9. ✓ Test install prompt
  10. ✓ Check caching strategies work correctly
  11. ✓ Verify update mechanism works
  12. ✓ Test cross-browser compatibility
Exercise:
  1. Build your PWA for production
  2. Deploy to Netlify using the CLI
  3. Configure netlify.toml with proper headers
  4. Test PWA installation on mobile device
  5. Run Lighthouse audit and fix any issues
  6. Deploy to Firebase Hosting as alternative
  7. Set up custom domain with HTTPS
  8. Implement cache versioning strategy
  9. Test service worker updates in production
  10. 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.