Firebase Hosting
Webホスティングプラットフォーム
Firebase Hosting
概要
Firebase HostingはGoogleが提供する高速で安全なWebホスティングサービスです。SPAやPWA向けに最適化され、Firebase他サービスとの統合によりフルスタック開発をサポートします。モバイルアプリ開発と連携したWebアプリケーションで人気で、Firebase Authentication、Firestore等との統合により、迅速なプロトタイプ開発で重宝されています。
詳細
2014年にGoogleに買収されたFirebaseの一部として提供されるFirebase Hostingは、モダンなWebアプリケーション開発に特化したホスティングサービスです。GoogleのグローバルCDNネットワークを活用し、世界中の180以上の場所から高速配信を実現。特にSPA(Single Page Application)やPWA(Progressive Web App)に最適化されており、自動SSL証明書、カスタムドメイン、ロールバック機能を標準提供。Firebase Authentication、Cloud Firestore、Cloud Functionsなどとの緊密な統合により、フロントエンドからバックエンドまでの完全なソリューションを提供します。
メリット・デメリット
メリット
- 高速グローバルCDN: Googleの180+エッジロケーションからの配信
- Firebase生態系との統合: Authentication、Firestore、Functions等との完全統合
- SPA/PWA最適化: モダンなWebアプリケーションに特化した機能
- 自動SSL証明書: Let's Encryptによる無料SSL
- 簡単なロールバック: ワンクリックでの前バージョン復元
- リアルタイム機能: Firebase Realtimeと組み合わせたライブ更新
- 無料利用枠: 月10GBまでの無料ストレージと転送量
デメリット
- Google依存: Googleサービスへの高い依存度
- 制限事項: 大容量ファイルや高トラフィックでの課金増加
- 学習コストの高さ: Firebase生態系全体の理解が必要
- サーバーサイド制限: 静的サイトとCloud Functions以外の制約
参考ページ
書き方の例
基本的なセットアップとプロジェクト設定
# Firebase CLIのインストール
npm install -g firebase-tools
# Firebaseにログイン
firebase login
# プロジェクトの初期化
firebase init hosting
# ローカル開発サーバー
firebase serve --only hosting --port 5000
# エミュレーター(全機能)の起動
firebase emulators:start
// firebase.json - Firebase設定
{
"hosting": {
"public": "dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "/api/**",
"function": "api"
},
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/service-worker.js",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
}
]
},
{
"source": "**/*.@(jpg|jpeg|gif|png|svg|webp)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000"
}
]
}
],
"cleanUrls": true,
"trailingSlash": false
},
"functions": {
"source": "functions",
"node": 18
},
"emulators": {
"hosting": {
"port": 5000
},
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"ui": {
"enabled": true,
"port": 4000
}
}
}
静的サイトデプロイ
# 本番デプロイ
firebase deploy --only hosting
# プレビューチャンネルでのデプロイ
firebase hosting:channel:deploy preview-feature
# 特定のプロジェクトへのデプロイ
firebase deploy --project my-project-id
# 複数サイトの管理
firebase target:apply hosting main my-main-site
firebase target:apply hosting admin my-admin-site
firebase deploy --only hosting:main
# .github/workflows/firebase.yml - GitHub Actions
name: Deploy to Firebase Hosting
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
projectId: my-project-id
channelId: live
フレームワーク統合(Next.js、React、Vue)
// next.config.js - Next.js + Firebase
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
images: {
unoptimized: true
},
// Firebase Hosting用の設定
assetPrefix: process.env.NODE_ENV === 'production' ? undefined : '',
// 環境変数の設定
env: {
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FIREBASE_API_KEY: process.env.FIREBASE_API_KEY,
},
};
module.exports = nextConfig;
<!-- Vue.js + Firebase統合 -->
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<button v-if="user" @click="logout">Logout</button>
<button v-else @click="login">Login</button>
</nav>
<main>
<router-view />
</main>
</div>
</template>
<script>
import { auth } from './firebase/config';
import { signInWithPopup, GoogleAuthProvider, signOut } from 'firebase/auth';
export default {
name: 'App',
data() {
return {
user: null
};
},
created() {
auth.onAuthStateChanged(user => {
this.user = user;
});
},
methods: {
async login() {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
} catch (error) {
console.error('Login failed:', error);
}
},
async logout() {
try {
await signOut(auth);
} catch (error) {
console.error('Logout failed:', error);
}
}
}
};
</script>
// firebase/config.ts - Firebase初期化
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getFunctions } from 'firebase/functions';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID
};
// Firebase初期化
const app = initializeApp(firebaseConfig);
// サービスの初期化
export const auth = getAuth(app);
export const db = getFirestore(app);
export const functions = getFunctions(app);
export const storage = getStorage(app);
export default app;
カスタムドメインとSSL
# カスタムドメインの追加
firebase hosting:sites:create my-custom-site
firebase target:apply hosting main my-custom-site
# ドメインの設定
firebase hosting:sites:list
firebase hosting:sites:get my-custom-site
# SSL証明書は自動で設定される(Let's Encrypt)
// firebase.json - マルチサイト設定
{
"hosting": [
{
"target": "main",
"public": "dist",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
{
"target": "admin",
"public": "admin-dist",
"rewrites": [
{
"source": "/admin/**",
"destination": "/admin/index.html"
}
]
}
]
}
// .firebaserc - プロジェクト設定
{
"projects": {
"default": "my-project-id",
"staging": "my-project-staging",
"production": "my-project-prod"
},
"targets": {
"my-project-id": {
"hosting": {
"main": ["my-main-site"],
"admin": ["my-admin-site"]
}
}
}
}
サーバーレスファンクションとAPI
// functions/index.js - Cloud Functions
const { onRequest } = require('firebase-functions/v2/https');
const { onDocumentCreated } = require('firebase-functions/v2/firestore');
// HTTP APIエンドポイント
exports.api = onRequest({
cors: true,
region: 'asia-northeast1'
}, async (req, res) => {
const { method, path } = req;
if (method === 'GET' && path === '/users') {
// Firestoreからユーザー一覧を取得
const admin = require('firebase-admin');
const db = admin.firestore();
try {
const snapshot = await db.collection('users').get();
const users = [];
snapshot.forEach(doc => {
users.push({ id: doc.id, ...doc.data() });
});
res.json({ users });
} catch (error) {
res.status(500).json({ error: error.message });
}
} else {
res.status(404).json({ error: 'Not found' });
}
});
// Firestoreトリガー
exports.onUserCreate = onDocumentCreated('users/{userId}', (event) => {
const snapshot = event.data;
const data = snapshot.data();
console.log(`New user created: ${data.name}`);
// ウェルカムメール送信等の処理
return null;
});
// src/services/api.ts - フロントエンド API クライアント
import { getFunctions, httpsCallable } from 'firebase/functions';
import { functions } from '../firebase/config';
class ApiService {
private functions = getFunctions();
// HTTP Callable Function
async getUsers() {
try {
const getUsersFunction = httpsCallable(this.functions, 'getUsers');
const result = await getUsersFunction();
return result.data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
}
// REST API呼び出し
async fetchUserData(userId: string) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching user data:', error);
throw error;
}
}
// リアルタイムデータリスニング
subscribeToUserUpdates(userId: string, callback: (data: any) => void) {
const { doc, onSnapshot } = require('firebase/firestore');
const { db } = require('../firebase/config');
const userDoc = doc(db, 'users', userId);
return onSnapshot(userDoc, callback);
}
}
export const apiService = new ApiService();
CI/CDと本番最適化
# .github/workflows/firebase-preview.yml - プレビューチャンネル
name: Firebase Preview Deploy
on:
pull_request:
branches: [ main ]
jobs:
build_and_preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Preview Channel
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
projectId: my-project-id
expires: 7d
id: firebase_preview
- name: Comment Preview URL
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🔥 Preview URL: ${{ steps.firebase_preview.outputs.details_url }}'
})
# Firebase CLI コマンド集
firebase projects:list # プロジェクト一覧
firebase use --add # プロジェクト追加
firebase hosting:sites:list # サイト一覧
firebase hosting:channel:list # チャンネル一覧
# ログとモニタリング
firebase functions:log # Functions ログ
firebase hosting:clone source-site-id destination-site-id # サイト複製
# セキュリティルール
firebase deploy --only firestore:rules # Firestore ルール
firebase deploy --only storage:rules # Storage ルール
# データベース操作
firebase firestore:delete --all-collections # 全データ削除(注意)
firebase firestore:indexes # インデックス管理
# パフォーマンス最適化
firebase hosting:disable # ホスティング無効化
firebase experiments:enable webframeworks # 実験的機能
// firebase.json - 高度な設定
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
// PWA設定
"rewrites": [
{
"source": "/sw.js",
"destination": "/sw.js"
},
{
"source": "**",
"destination": "/index.html"
}
],
// セキュリティヘッダー
"headers": [
{
"source": "**",
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
}
]
}
],
// リダイレクト設定
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"type": 301
}
],
// A/Bテスト設定
"appAssociation": "AUTO",
"cleanUrls": true,
"trailingSlash": false
},
// Remote Config
"remoteconfig": {
"template": "remoteconfig.template.json"
},
// App Distribution
"appDistribution": {
"serviceAccountFile": "path/to/service-account.json",
"releaseNotesFile": "RELEASE_NOTES.md"
}
}