Skip to main content

Multi-Site Hosting with Firebase

Manage multiple websites within a single Firebase project. Perfect for marketing sites, documentation, blogs, and microsites all under one project umbrella.

When to Use Multi-Site Hosting

Good Use Cases ✅

  • Marketing site + App - example.com + app.example.com
  • Documentation sites - docs.example.com
  • Blog platforms - blog.example.com
  • Regional sites - uk.example.com, de.example.com
  • Staging environments - staging.example.com
  • Microsites - Campaign or event-specific sites

When to Use Separate Projects ❌

  • Different billing requirements
  • Separate team permissions needed
  • Completely unrelated sites
  • Different security requirements

Setting Up Multiple Sites

Step 1: Create Additional Sites

# Create new sites (max 36 sites per project)
firebase hosting:sites:create app-site
firebase hosting:sites:create blog-site
firebase hosting:sites:create docs-site

# List all sites
firebase hosting:sites:list

Site IDs must be globally unique and will get URLs like:

  • app-site.web.app
  • blog-site.web.app
  • docs-site.web.app

Step 2: Configure Targets

# Apply targets to map sites to deploy targets
firebase target:apply hosting app app-site
firebase target:apply hosting blog blog-site
firebase target:apply hosting docs docs-site

# View current targets
firebase target:list

Step 3: Update firebase.json

{
"hosting": [
{
"target": "app",
"public": "dist/app",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [{
"source": "**",
"destination": "/index.html"
}],
"headers": [{
"source": "**/*.@(js|css)",
"headers": [{
"key": "Cache-Control",
"value": "max-age=31536000"
}]
}]
},
{
"target": "blog",
"public": "dist/blog",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"cleanUrls": true,
"trailingSlash": false,
"headers": [{
"source": "**/*.@(jpg|jpeg|gif|png)",
"headers": [{
"key": "Cache-Control",
"value": "max-age=86400"
}]
}]
},
{
"target": "docs",
"public": "dist/docs",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"redirects": [{
"source": "/v1/**",
"destination": "https://legacy-docs.example.com/v1/:splat",
"type": 301
}]
}
]
}

Project Structure

Monorepo Structure

my-project/
├── apps/
│ ├── main/
│ │ ├── src/
│ │ ├── public/
│ │ └── package.json
│ ├── blog/
│ │ ├── content/
│ │ ├── themes/
│ │ └── package.json
│ └── docs/
│ ├── docs/
│ ├── static/
│ └── package.json
├── shared/
│ └── components/
├── firebase.json
├── .firebaserc
└── package.json

Build Configuration

Root package.json:

{
"scripts": {
"build:app": "cd apps/main && npm run build",
"build:blog": "cd apps/blog && npm run build",
"build:docs": "cd apps/docs && npm run build",
"build:all": "npm run build:app && npm run build:blog && npm run build:docs",
"deploy:app": "npm run build:app && firebase deploy --only hosting:app",
"deploy:blog": "npm run build:blog && firebase deploy --only hosting:blog",
"deploy:docs": "npm run build:docs && firebase deploy --only hosting:docs",
"deploy:all": "npm run build:all && firebase deploy --only hosting"
}
}

Deployment Strategies

Deploy Individual Sites

# Deploy single site
firebase deploy --only hosting:app
firebase deploy --only hosting:blog

# Deploy multiple sites
firebase deploy --only hosting:app,hosting:blog

# Deploy all sites
firebase deploy --only hosting

CI/CD for Multi-Site

# GitHub Actions example
name: Deploy Multi-Site
on:
push:
branches: [main]
paths:
- 'apps/**'
- 'firebase.json'

jobs:
deploy-app:
if: contains(github.event.head_commit.modified, 'apps/main/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci && npm run build:app
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
target: app
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'

deploy-blog:
if: contains(github.event.head_commit.modified, 'apps/blog/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci && npm run build:blog
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
target: blog
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'

Custom Domains

Setting Up Domains

# Each site can have custom domains
# Configure in Firebase Console or via API

# Example domain mapping:
# app-site → app.example.com
# blog-site → blog.example.com
# docs-site → docs.example.com
# main-site → example.com, www.example.com

DNS Configuration

For each subdomain:

Type: A
Name: app
Value: Firebase IP addresses

Type: AAAA
Name: app
Value: Firebase IPv6 addresses

Type: TXT (for verification)
Name: app
Value: firebase=YOUR_VERIFICATION_CODE

Domain Management Tips

  1. Verify root domain first - Makes subdomain verification easier
  2. Use CNAME for subdomains - When possible, simpler than A records
  3. Plan SSL provisioning time - Can take up to 24 hours
  4. Set up redirects - www to non-www or vice versa

Preview Channels for Multi-Site

Site-Specific Previews

# Create preview channel for specific site
firebase hosting:channel:deploy preview --only hosting:app
firebase hosting:channel:deploy feature-x --only hosting:blog

# URLs will be:
# app-site--preview-HASH.web.app
# blog-site--feature-x-HASH.web.app

Cross-Site Testing

Test interactions between sites:

// In app site, reference blog preview
const blogUrl = process.env.REACT_APP_BLOG_URL || 'https://blog.example.com';

// For preview testing
// REACT_APP_BLOG_URL=https://blog-site--preview-x8yn.web.app

Shared Resources

Sharing Assets

{
"hosting": [{
"target": "app",
"public": "dist/app",
"rewrites": [{
"source": "/shared/**",
"destination": "https://assets-site.web.app/shared/:splat"
}]
}]
}

Shared Authentication

// Configure Firebase Auth to work across subdomains
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL);

// Set auth domain in Firebase Console
// authDomain: "example.com" (not app.example.com)

Cross-Site Communication

// PostMessage for cross-origin communication
// From app.example.com to blog.example.com
window.postMessage({
type: 'user-login',
userId: user.uid
}, 'https://blog.example.com');

// Listen on blog.example.com
window.addEventListener('message', (event) => {
if (event.origin !== 'https://app.example.com') return;
if (event.data.type === 'user-login') {
// Handle login state
}
});

Performance Optimization

Site-Specific Optimization

{
"hosting": [
{
"target": "app",
"headers": [{
"source": "**/*.js",
"headers": [{
"key": "Cache-Control",
"value": "max-age=31536000, immutable"
}]
}]
},
{
"target": "blog",
"headers": [{
"source": "**/*.html",
"headers": [{
"key": "Cache-Control",
"value": "max-age=3600, s-maxage=86400"
}]
}]
}
]
}

Resource Prioritization

<!-- Preconnect to other sites -->
<link rel="preconnect" href="https://app.example.com">
<link rel="preconnect" href="https://blog.example.com">

<!-- DNS prefetch for external resources -->
<link rel="dns-prefetch" href="https://cdn.example.com">

Monitoring Multi-Site

Unified Analytics

// Track cross-site user journeys
gtag('config', 'GA_MEASUREMENT_ID', {
cookie_domain: 'example.com',
cookie_flags: 'SameSite=None;Secure',
linker: {
domains: ['example.com', 'app.example.com', 'blog.example.com']
}
});

Site-Specific Monitoring

# Monitor individual site metrics
# In Firebase Console → Hosting → Select site

# CLI commands for specific sites
firebase hosting:sites:get app-site
firebase hosting:versions:list --site blog-site

Best Practices

1. Naming Conventions

# Use clear, consistent naming
main-site # Main marketing site
app-site # Application
blog-site # Blog
docs-site # Documentation
staging-site # Staging environment

2. Build Optimization

// Share dependencies with workspaces
// package.json
{
"workspaces": [
"apps/*",
"shared/*"
]
}

3. Deployment Strategy

  • Deploy incrementally, not all sites at once
  • Use preview channels for testing
  • Implement proper rollback procedures
  • Monitor each site independently

4. Cost Management

  • Monitor bandwidth per site
  • Optimize assets for each site's needs
  • Consider CDN caching strategies
  • Review usage regularly

Troubleshooting

Common Issues

IssueSolution
"Site ID already exists"Choose globally unique ID
Deploy affects wrong siteCheck target configuration
Domain verification failsVerify DNS propagation
Cross-site auth issuesConfigure auth domain correctly
Preview channels mixed upUse --only hosting:TARGET

Debug Commands

# Check site configuration
firebase hosting:sites:get SITE_ID

# Verify targets
firebase target:list

# Test specific site locally
firebase emulators:start --only hosting
# Visit localhost:5000 and localhost:5001 for different sites

Migration Guide

Moving from Single to Multi-Site

  1. Backup current configuration

    cp firebase.json firebase.json.backup
  2. Create new site

    firebase hosting:sites:create new-main-site
  3. Update configuration

    • Convert single hosting to array
    • Add target configuration
    • Update build scripts
  4. Test thoroughly

    • Deploy to preview channels first
    • Verify all redirects work
    • Check custom domains
  5. Switch DNS

    • Update DNS records to new site
    • Monitor for issues

Next Steps


Pro Tip: Start with 2-3 sites and expand as needed. Multi-site hosting is powerful but requires good organization. Use consistent naming and clear documentation! 🚀