14
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Firebase Hostingのフォワード先に、どのようなヘッダーを渡していくのか

Last updated at Posted at 2020-12-12

Firebaseでは、Firebase Hostingの構成をカスタマイズすることで、特定のURLパターンをCloud FunctionsやCloud Runに処理させることが出来ます。

firebase.json
{
  "hosting": {
    "public": "packages/site/dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/fun",
        "function": "http"
      },
      {
        "source": "/run",
        "run": {
          "serviceId": "container",
          "region": "asia-northeast1"
        }
      }
    ]
  }
}

※上の例では、/funから始まるURLは全てCloud Functionsに、/runから始まるURLは全てCloud Runに処理させています

去年もこんなことをしてたし、今年はこんな発表もしてたりで、相変わらずFirebaseを使っています。
社内でも相変わらずプレビュー環境は運用中です。

いろいろなタイミングで使って見るたびに「ヘッダーで何取れたっけ?」と考えたりしているので、一度どんなリクエストが流れてくるのか整理してみることにしました。

準備する

準備するとは言っても、今回用意するのは次の3項目だけです。

  • ヘッダーをダンプするためのサーバーアプリケーション
  • Cloud Functionsのエンドポイント定義
  • Cloud Runのコンテナ動作

Firebase上での実装となるため、Expressを使います。
ソース全体はGitHubリポジトリにて

本体: app.ts

とりあえずの情報確認なので、シンプルにリクエスト時のパスとヘッダーのみをJSONにしてエコーするだけのシンプルな実装です。

app.ts
import express from 'express';

export const app = express();

const router = express.Router();
app.use(router);

type Content = {
  path: string;
  headers: { [key: string]: string; };
}

router.get('*', (req, res) => {
  let content = {
    path: req.path,
    headers: req.headers,
  };
  res.send(JSON.stringify(content));
});

デプロイする

作業の流れとしては、こうなります。

  1. Firebaseプロジェクトを作成して、Blazeプランにする
  2. そのプロジェクトのGCPコンソールに移動して、Cloud Runを有効にする
  3. ローカルでDockerイメージを作成してプッシュする
  4. Cloud Runで検証用のサービスrunを作成する
  5. Firebaseのデプロイを一式行う

確認する

Cloud Functions/Cloud RUN直接の経路も合わせて確認してみましょう。
というわけで、確認してみるURLは以下のとおりとなります。

  • Cloud Functions
    • 直接: https://us-central1-PROJECT_ID.cloudfunctions.net/fun
    • 中継: https://PROJECT_ID.web.app/fun
  • Cloud Run
    • 直接: https://run-XXXXX.a.run.app/run
    • 中継: https://PROJECT_ID.web.app/run

PROJECT_IDは自身のFirebaseプロジェクトID、XXXXXはGCPが付与したランダムな文字列、がそれぞれ入ります

これらのURLに対して、ローカルマシン上のHTTPieでアクセスしてレスポンスの中身を見てみることにします。

幕間:HTTPie

Python製のCLIT HTTPクライアントです。curlwgetのような使い方を目的としていますが、JSONだったら適切に加工表示してくれたりするので、単純な確認などに重宝します。

(こんな感じに表示してくれます)

» http https://run-7zw4c6v9nq-uc.a.run.app/run
HTTP/1.1 200 OK
Alt-Svc: h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Content-Length: 356
Date: Wed, 09 Dec 2020 11:48:43 GMT
Server: Google Frontend
X-Cloud-Trace-Context: f8fefa1ccbb8ec95e3381779c285a7cf;o=1
content-type: text/html; charset=utf-8
etag: W/"164-Y0NZ+ieZbD6GyZVOnkGU3NtR1cI"
x-powered-by: Express

{
    "headers": {
        "accept": "*/*",
        "accept-encoding": "gzip, deflate",
        "content-length": "0",
        "forwarded": "for=\"127.0.0.1\";proto=https",
        "host": "run-7zw4c6v9nq-uc.a.run.app",
        "user-agent": "HTTPie/2.0.0",
        "x-cloud-trace-context": "f8fefa1ccbb8ec95e3381779c285a7cf/11614369908781814861;o=1",
        "x-forwarded-for": "127.0.0.1",
        "x-forwarded-proto": "https"
    },
    "path": "/run"
}

確認してみたサマリ

※補足:HTTPie経由での結果を元に表を出しているため、例えばブラウザなどでは別のヘッダーが出てくることもあります 1

/fun直接 /fun中継 /run直接 /run中継
パス / /fun /run /run
accept o o o o
accept-encoding o o o o
accept-language - o o o
cache-control - o - o
cdn-loop - o - o
connection o o - -
content-length - - o o
fastly-client - o - o
fastly-client-ip - o - o
fastly-ff - o - o
fastly-orig-accept-encoding - o - o
fastly-ssl - o - o
fastly-temp-xff - o - o
forwarded o o o o
function-execution-id o o - -
host o o o o
pragma - o - o
transparent o o - -
user-agent o o o o
via - o - o
x-appengine-city o - - -
x-appenginecitylatlong o - - -
x-appengine-country o o - -
x-appengine-default-version-hostname o o - -
x-appengine-https o o - -
x-appengine-region o - - -
x-appengine-request-log-id o o - -
x-appengine-timeout-ms o o - -
x-appengine-user-ip o o - -
x-cloud-trace-context o o o o
x-country-code - o - o
x-firebase-channel - o - o
x-firebase-hosting-channel - o - o
x-forwarded-for o o o o
x-forwarded-host - o - o
x-forwarded-proto o o o o
x-forwarded-server - o - o
x-forwarded-url - o - o
x-timer - o - o
x-varnish - o - o

ちょっと考察してみる

軽く眺めつつ、グルーピングしたりして考察してみましょう。

fastly-

こちらにもあるとおり、Firebase HostingではエッジとしてFastlyが使われています。
そのため、fastly-から始まるヘッダーが追加付与されています。
普段の自分的にあまり有効利用できないのですが、fastly-client-ipには確実にクライアントIPが載ってくるっぽいので、扱うならその辺りでしょうか。

x-appengine-

こちらは、Cloud Functions系のリクエスト時にアプリケーションに流れてくるヘッダー群です。
目に付くのは、*「多くの位置情報系のヘッダーが消滅する」「x-appengine-countryZZになる」*でしょうか。

これは、クライアント->Functionsという通信経路がクライアント->Firebase->Functionsになったことで、
Cloud Functionsの位置情報判定を取らなくなったのかなと思います。

もし、位置情報をここで取るようなプロジェクトだと、注意が必要かもしれませんね。
※一応x-country-codeで国レベルの場所だけは取れるみたいです。

x-firebase-

x-firebase-channel x-firebase-hosting-channel の2個があるんですが、
何度か試した感じではいずれも liveという値のみが入ってたので、ちょっと使いどころは不明ですね...

x-forwarded-

よくあるヘッダーです。x-forwarded-for,x-forwarded-proto辺りは途中で必ず用意されるようにはなってます。
Firebase Hosting経由でアクセスした場合では、x-forwarded-hostが付与されるようになるため、
サービスFQDNをアプリケーションで知りたい場合はこのヘッダーを見てあげましょう。(自分がよく向き合っている項目)
なお、hostヘッダはーそれぞれのサービスのFQDNがそのまま載ってきます。

Cloud Functionsにおけるパス情報

Cloud Functionsでは、直接リクエストとFirebase Hosting経由で違いがあります。

Cloud Functionsでは同一プロジェクト/リージョンにある複数の関数でFQDNを使いまわしている関係で、
アプリケーション自身が認識しているルートパスが1階層下になる感じですね。

Firebase Hosting経由を前提に開発しているなら気にしなくて良いのですが、
逆にCloud Functions直接展開を想定している時には、ローカル開発で気をつける必要がありそうです。

まとめ

**とはいえなんですが、**Firebaseを雰囲気で使っているので、実はガッツリ「このヘッダーを積極活用する!」というのがあまりなかったりします。

  • Webアプリケーションとして考えるとX-Forwarded-系が適切に提供されているので、基本的にこれを活用すれば良い
  • 位置情報系も、基本的に位置情報ライブラリを使って取ったほうが早い

今回自分の場合は、「Cloud Runの内部でFirebase側のFQDNをきちんと知る必要がある」という理由がきっかけで調べてみました。
普段ふわっと使っている領域を半歩深堀りするのは意義があるし、楽しいですね。

  1. 例として、クライアントからのリクエスト時点でaccept-languageヘッダーがある場合は、Firebase Hosting 経由ではx-orig-accept-languageが付与されるらしいです

14
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?