[Firebase] 静的なWebサイトをホスティングする

FirebaseのHostingを利用すれば基本無料で静的なサイトを運用できます。 独自ドメインも追加でき、何らならSSL証明書も無料でついてくるという太っ腹。もしサイトが成長し膨大なアクセスが発生した場合は課金する必要がありますが、こちらがプラン変更しない限り料金は一切発生しません。

ドメインやDNSの準備や設定、本番へデプロイ(反映)するにはコマンドを叩く必要がありますが、それさえ乗り越えてしまえばさほど難しくはありません。

環境の準備

プロジェクトの作成

サイトを設置するためのプロジェクトが必要になります。 プロジェクトの作成方法は以下のページを参照くださいませ。 blog.katsubemakito.net

Node.jsのインストール

Hostingの利用は原則としてTerminalなどのCLI上でコマンドを叩いて行いますので、まずはその準備を行います。

Node.jsがインストールされている必要があります。公式サイトからダウンロードしインストールしてください。「推奨版」と書かれている方でOKです。 https://nodejs.org/ja/nodejs.org

もしmacOSを利用されている場合で、凝った設定をしたい場合は以下のページを参照くださいませ。 blog.katsubemakito.net

FirebaseのCLIツールをインストール

Node.jsをインストールするとnpmという、様々なライブラリやツールを取得できる便利なツールが同時に入るのですが、これを使ってインストールを行います。

$ npm install -g firebase-tools

今回は以下のバージョンが入りました。

$ firebase --version
6.3.0

Googleアカウントでログイン

Terminalなどでfirebase loginコマンドを打ちます。 すると「利用状況を収集しても良い?匿名(あなたが特定されることはない)だよ」と聞いてくるので、YesかNoのいずれか入力します。

$ firebase login
? Allow Firebase to collect anonymous CLI usage and error reporting information? (Y/n)

Webブラウザが起動しログインを求められます。 すでにGoogleアカウントでログインしている場合はどのユーザーでFirebaseを利用するか選びます。

OAuthの許可画面が出てきましたね。移譲する権限に問題なければ画面下の「許可」ボタンをクリック。

以下の画面になればブラウザでの操作は終了です。ウィンドウを閉じます。

Terminalに戻り以下のようなメッセージが表示されていれば成功となります。

✔  Success! Logged in as katsubemakito@gmail.com

サイトを公開する

プロジェクトの作成

適当な名前のディレクトリを作成し、カレントディレクトリを移動します。

$ mkdir miku3net
$ cd miku3net

Firebaseで利用できるよう現在のディレクトリを初期化します。 firebase initコマンドを打つと派手なAAが表示されますw

$ firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/katsube/Desktop/miku3net

このフォルダをFirebaseのどの機能で利用するか聞いてくるので、「Hosting」にカーソルを合わせスペースキーを押した後にエンターキーを押します。スペースを押さないと選択したことになりません。

? Which Firebase CLI features do you want to setup for this folder? Press Space to select feat
ures, then Enter to confirm your choices.
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◯ Functions: Configure and deploy Cloud Functions
❯◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules

続けてどのプロジェクト利用するか聞いてきますので、先ほど異なりカーソルを合わせたらそのままエンターキーで選択します。UIは統一してくれwww

 Select a default Firebase project for this directory:
❯ [don't setup a default project]
  test-f76bc (test)
  [create a new project]

次に一般公開するディレクトリ名を入力します。こだわりがなければそのままエンターキーを押すとpublicという名前のディレクトリが作成され、その中にファイルを入れれば公開されます。ここではデフォルトのままにしました。

? What do you want to use as your public directory? (public)

最後にSPA(シングルページアプリケーション)か聞いてくるので答えます。今回はNoにしました。

? Configure as a single-page app (rewrite all urls to /index.html)? (y/N)

次のようなメッセージが表示され、最後にcomplete!の文字が出ていれば成功です。おめでとうございます!

✔  Wrote public/404.html
✔  Wrote public/index.html

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...
i  Writing gitignore file to .gitignore...

✔  Firebase initialization complete!

実際に作成されたファイルやフォルダも確認しておきましょう。 以下のようにFirebaseの設定を記録するfirebase.jsonと、一般公開されるpublicフォルダ、その中にサンプル的なHTMLファイルが生成されているのがわかります。

実は上記以外にも不可視ファイルがいくつか作成されています。 これらも必要に応じて利用していきますが、いったんここでの説明は割愛します。

$ ls -la
total 24
drwxr-xr-x   8 katsube  staff   256  1 30 02:34 ./
drwx------+ 39 katsube  staff  1248  1 30 02:04 ../
drwxr-xr-x   3 katsube  staff    96  1 29 20:56 .firebase/
-rw-r--r--   1 katsube  staff    84  1 30 02:34 .firebaserc
-rw-r--r--   1 katsube  staff  1144  1 29 20:50 .gitignore
-rw-r--r--   1 katsube  staff   134  1 29 20:50 firebase.json
drwxr-xr-x   4 katsube  staff   128  1 30 01:15 public/

公開する

コマンド一発で現在のファイルを公開できます。

$ firebase deploy

ずらずらーとメッセージが表示され最終的にDeploy complete!が表示されれば成功です。最後にHosting URLが表示されていますので、こちらにブラウザでアクセスしてみましょう。

=== Deploying to 'test-f76bc'...

i  deploying hosting
i  hosting[test-f76bc]: beginning deploy...
i  hosting[test-f76bc]: found 2 files in public
✔  hosting[test-f76bc]: file upload complete
i  hosting[test-f76bc]: finalizing version...
✔  hosting[test-f76bc]: version finalized
i  hosting[test-f76bc]: releasing new version...
✔  hosting[test-f76bc]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/test-f76bc/overview
Hosting URL: https://test-f76bc.firebaseapp.com

以下のような表示がされているでしょうか?ApacheのIt's work的なページですね。

Firebaseの管理画面に行くとデプロイした履歴を確認することもできます。

このあとは自分が本来アップしたかったファイルなどに差し替えて、もう一度firebase deployコマンドを打ってあげる流れになります。

独自ドメインを設定する

DNSは自前で用意する必要がありますが、独自ドメインも簡単に設定することができます。なんと無料のSSL証明書も自動でついてきます!

ドメインを登録する

FirebaseのHostingの管理画面に移動し、右側にある「ドメインを接続」ボタンをクリック

設定するドメイン名を入力し「次へ」ボタンをクリック

ここでドメインが本当に自分の物か認証が入ります。DNSに指定されたTXTレコードを追加してあげます。

たまたま「ムームードメイン」の「ムームーDNS」で管理していたドメインだったので、そのまま設定しちゃいました。ここでAレコードなどが残っていると次の画面でエラーとなり先に進めなくなるのでこのタイミングで削除してください。  ※以下はFirebaseとは関係ない別サービスの画面です。

念の為nslookupコマンドでTXTレコードが無事に追加できたか確認します。大丈夫そうですね。

$ nslookup -q=txt miku3.net
Server:         10.7.138.99
Address:        10.7.138.99#53

Non-authoritative answer:
miku3.net       text = "google-site-verification=VYPJ1QYkAsJXihJltQL2hRyklnUTA2RczcJyk6D0pJw"

Firebaseにもどり画面を進めます。DNSの設定に問題がなければ最終的にAレコードを登録せよと指示されるので、そのとおり登録します。

しました。

これでいったん設定は完了なのですが、もしドメインの頭にwwwをつけたい場合は以下の設定も行っておくと良いでしょう。

ダッシュボードを見ると「保留中」となっています。 Firebase内で処理が行われているようですので、完了するまでしばらく待つ必要があります。……最大で24時間とか言ってるけど実際にはすぐにできるんだろ?と思いたいところですが、ガチで長時間かかりますw タイミングにもよるでしょうが就寝前や退社前などにやるのが精神的に良いかもしれませんw

「そんな長時間何をやってるんだ?」というと、どうやらSSL証明書をGoogleの認証局に登録するなどしているようです。巨大なシステムですからね、浸透するまで時間がかかるのでしょう。

ドメイン所有権を確認したら、ドメインの SSL 証明書がプロビジョニングされ、グローバル CDN にデプロイされます。このプロセスには数時間かかることがあります。

公式ドキュメントより

その後、10〜12時間ほど正座待機しているとステータスが「接続されています」に変更になり、無事に完了したようです。時間はタイミングによって変わると思いますので参考程度に。

ブラウザでアクセスするとSSL証明書が適用されているのが確認できます。

登録中にエラーが発生したら?

「ドメインを追加できません。別のプロジェクトにすでに登録されている可能性があります」と表示されて進めなくなった場合、DNSに古いAレコードが残っている可能性があります。削除してもう一度トライしてみてください。

このエラーメッセージだと「Firebase初めて触るんですけど!!ヽ(`Д´)ノプンプン」となりますよねw そこはグッとこらえてくださいw

その他

使用できないファイル名

Firebase Hostingでは/__から始まるファイルやディレクトリは作成できない仕様になっています。 設定などで回避できませんので、かぶっている場合は変更する必要があります。

ちなみに以下の様なFirebaseのJavaScript用のSDKが設置されており、こちらを利用することも可能です。

<script src="/__/firebase/5.4.1/firebase-app.js"></script>
<script src="/__/firebase/5.4.1/firebase-auth.js"></script>
<script src="/__/firebase/5.4.1/firebase-storage.js"></script>

詳細はドキュメントをご確認ください。 firebase.google.com

ローカルで動作確認する

実際Webサイトを作っていていきなり本番へdeployすることはありませんよねw Firebase CLIではローカルでWebサーバを起動し確認できる仕組みが用意されています。

プロジェクトのディレクトリに移動し以下のコマンドを打つだけです。起動するまで少しだけ時間がかかりますので何も反応がなくても慌てなくて大丈夫です。

$ firebase serve

最終的に以下の表示が出れば起動に成功しています。

i  hosting: Serving hosting files from: public
✔  hosting: Local server: http://localhost:5000

あとは任意のブラウザで http://localhost:5000 にアクセスするだけです。

サーバを終了したいときは、Ctrl+cで強制終了します。

^CShutting down...
$ 

デプロイ先を切り替える

例えば「ステージング環境」「本番環境」など複数の環境を用意し、指定した環境にデプロイしたいといったことも可能です。 ちょっと面倒なんですが、環境ごとにプロジェクトを用意する必要があります。今回は新たに「staging-fc0b6」という名前のプロジェクトを作成しました。

プロジェクトの登録

以下のコマンドで先ほど新たに作成したプロジェクトを追加します。

$ firebase use --add

すると現在利用できるプロジェクト一覧が表示されますので、先ほど作成したプロジェクトにカーソルを合わせてエンターキーを押します。

? Which project do you want to add? (Use arrow keys)
❯ staging-fc0b6 
  test-f76bc 

その後エイリアスとして利用する名前の入力を求められるので、ここではstagingと入力しました。

What alias do you want to use for this project? (e.g. staging) 

firebase listコマンドで、現在利用できるプロジェクトと名称、また現在どのプロジェクトが指定されているか確認できます。無事に追加されているのがわかりますね。

$ firebase list
┌───────────────────┬───────────────────────┬─────────────┐
│ Name              │ Project ID / Instance │ Permissions │
├───────────────────┼───────────────────────┼─────────────┤
│ test              │ test-f76bc            │ Owner       │
├───────────────────┼───────────────────────┼─────────────┤
│ staging (current) │ staging-fc0b6         │ Owner       │
└───────────────────┴───────────────────────┴─────────────┘

プロジェクトを切り替える

環境を切り替えるときは以下のようにfirebase use (環境名)と入力します。その後環境が切り替わったら、普通にfirebase deployすれば現在の環境にファイルが反映されます。

$ firebase use staging

なお、これらの設定は.firebasercというファイルに記録されていますので、名称を変更したり追加したりはこのファイルを直接編集しても良いかもしれません。

 $ cat .firebaserc
{
  "projects": {
    "default": "test-f76bc",
    "staging": "staging-fc0b6"
  }
}

近未来のお話

そう遠くない将来には、ひとつのプロジェクト内で簡単にスイッチングできそうです。試しにやってみましたが、現状だとfirebase deploy --only hosting:stagingなどと実行すると最終的にエラーとなりデプロイされませんでした。 firebase.google.com

HTTPヘッダを操作する

firebase.jsonを編集することでHTTPヘッダを操作することが可能です。

Access-Control-Allow-Origin

CORSですね。JavaScriptを利用し他のドメインからjsonファイルなどを参照したい場合、通常はブラウザのセキュリティの関係でアクセスすることができません。それを開放するのがこのヘッダになります。

firebase.jsonheadersの項目を追加しました。追加する場所がhostingの中である点に注意が必要です。

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "headers": [ {
      "source" : "foo.json",
      "headers" : [ {
        "key" : "Access-Control-Allow-Origin",
        "value" : "*"
      } ]
    }]
  }
}

対象とするファイルも適当に用意。

$ cat public/foo.json
{
  "message": "HelloWorld"
}

準備が終わったらfirebase deployし確認すると無事に出力されているのがわかります。先ほどのheadersをいじってdeployすると出力されなくなるのも確認すると挙動が確かに変化していることがわかると思います。

$ curl -i -s 'https://miku3.net/foo.json' | grep 'access-control-allow-origin'
access-control-allow-origin: *

Cache-Control

ブラウザ内部にキャッシュし、指定した時間内は再度リクエストを送って来ないよう設定します。

先ほどと同様にfirebase.jsonに項目を追加しました。ここでは7200秒(120分)の間、ブラウザにキャッシュせよという指示になります。

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "headers": [ {
      "source" : "foo.json",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=7200"
      } ]
    }]
  }
}

firebase deployして確認すると出力されているのがわかりますね。

$ curl -i -s 'https://miku3.net/foo.json' | grep 'cache-control'
cache-control: max-age=7200

通常は画像などに利用することが多いと思いますが、その場合はsourceの箇所を以下の用に指定すると、すべてのディレクトリにある"jpg", "jpeg", "gif", "png"が拡張子のファイルをすべて対象とすることが可能です。

    "headers": [ {
      "source" : "**/*.@(jpg|jpeg|gif|png)",

なおキャッシュするかどうかはクライアントや環境に依存しますので、絶対にキャッシュしてくれるとは限りませんのでご注意を。

同時に指定するには?

headersの配列の中に並べて記述してあげます。

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "headers": [
      {
        "source" : "foo.json",
        "headers" : [
           {
             "key" : "Access-Control-Allow-Origin",
             "value" : "*"
           },
           {
             "key" : "Cache-Control",
             "value" : "max-age=7200"
           }
         ]
      },
      {
        "source" : "**/*.@(jpg|jpeg|gif|png)",
        "headers" : [ {
          "key" : "Cache-Control",
          "value" : "max-age=7200"
        }]
      }
    ]
  }
}

対象ファイルをワイルドカードで指定する

対象とするファイルを指定する際には以下のようなワイルドカードを用いることができます。 以下は公式ドキュメントの説明をそのまま引用させていただきました。

  • ** は、任意のサブディレクトリ内にあるファイルまたはフォルダと一致します。* はルート ディレクトリ内のファイルとフォルダにのみ一致することに注意してください。
  • **/.* は、任意のサブディレクトリ内にある「.」で始まるファイル(通常は .git フォルダなどの非表示ファイル)と一致します。
  • **/*.@(jpg|jpeg|gif|png) は、任意のサブディレクトリ内にあるファイルで、「.」に続いて「jpg」、「jpeg」、「gif」、「png」のいずれかで終了するものと一致します。

詳細は公式ドキュメントの「glob パターン マッチング」の項目をご覧ください。 firebase.google.com

リダイレクトの設定を行う

firebase.jsonを編集することでリダイレクトの設定が行えます。

以下のようにredirectsを追加しました。ここでは/hoge.jsonにアクセスすると/foo.jsonへリダイレクトすうよう促されます。

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "redirects": [ {
      "source" : "/hoge.json",
      "destination" : "/foo.json",
      "type" : 301
    }]
  }
}

typeではHTTPのステータスコードを指定します。 ここでは301を指定していますが、完全にお引越しした際などに用います。一時的なリダイレクトであれば302を指定することになります。

ステータスコード 意味
301 Moved Permanently (永久に移動した)
302 Moved Temporarily (一時的な移動)

実際にアクセスすると以下のような結果になります。locationヘッダで移動を促されていますね(見やすいように一部ヘッダを消しています)

$ curl -i -s 'https://miku3.net/hoge.json'
HTTP/2 301 
server: nginx
content-type: application/octet-stream
location: /foo.json
accept-ranges: bytes
date: Wed, 30 Jan 2019 03:20:44 GMT
content-length: 24

Redirecting to /foo.json

一時的に公開を停止する

コマンド一発です。 「え、本当に止めるの?」と聞いてくるのでそのままエンターキーを押します。

$ firebase hosting:disable
? Are you sure you want to disable Firebase Hosting?

アクセスすると「Not Found」と表示されます。

ちなみに再開したいときはdeployコマンドを実行します。変更したファイルがなくても大丈夫です。最初はfirebase hosting:enableかなと思って試しに打ってみたら、そんなコマンド存在しないと怒られて焦りましたw

ダッシュボード上でも停止時間などが確認できます。「無効」と表示されているんが停止時、その上の行が再開するためにdeployコマンドを打ったときの物です。

ロールバックする

何らかの事情で急遽、過去のバージョンに戻したい場合、ダッシュボードから切り戻すこともできます。

「リリース履歴」にマウスを持っていくと右側に「」が洗われるので、これをクリックすると「ロールバック」という項目が現れます。

クリックするとほぼ一瞬でその時点に戻ります。

確実に動いていたバージョンに一瞬で戻せますので、これで凌いでいる間に原因の究明や復旧作業などを裏側で行い、解決し次第デプロイすることが可能です。

なお、この「リリース履歴」は最低限の情報しか表示されませんので、後述しますがGitやSubversionなどでバージョン管理は必須ですw これがあればGitいらないじゃんとはなりませんのでご注意をw

Gitで管理する

Firebaseと直接関係ないのですが、SSL証明書の反映待ちなど長い待機時間のうちにバージョン管理システムも入れておきましょう。転ばぬ先の杖じゃないですが取り返しがつかない事態になる前に設定を。

今回はGitHubにmiku3.netという名前のリポジトリを作成しておきました。カレントディレクトリをプロジェクト内に移動し、git initで初期化、そのままgit commitします。

$ git init
$ git add . 
$ git commit -m '1st commit'

ここではGitHubに作成したリモートリポジトリを登録し、先ほどcommitした内容をpushしています。

$ git remote add origin git@github.com:katsube/miku3.net.git
$ git push -u origin master

pushしたらGitHubのリポジトリのページを再読込し、送信した内容が表示されれば一安心。

少なくともfirebase deployする前にはcommitしておきたいですよね。万が一何かあった場合も簡単に巻き戻すことができます。

なお.gitignoreはfirebaseがプロジェクト作成時に気を利かせて作ってくれた物です。他にもGitへ登録したくないファイルがあればこのファイルを更新しましょう。

参考ページ

firebase.google.com firebase.google.com firebase.google.com firebase.google.com