beta

node.jsのExpressでCloudflareのキャッシュを効かせる

node.jsのExpressで作ったAPIで、データが固定のものをCloudflareのCDNにキャッシュをさせようとしたら、デフォルトではキャッシュが効かなかったので、キャッシュさせる方法のまとめです。

公開日:2019年6月19日

環境

  • node.js: v10.15.3
  • npm: 6.4.1

アプリの概要

node.jsサーバーで構築したExpress製のアプリ向けAPIです。フロントはVue.jsで、データの部分を担うAPIを作りました。

使っているプラグインなど

Expressとmysql、

{
  "name": "nodejs-express",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "express": "^4.16.4",
    "mysql": "^2.17.1",
  },
  "scripts": {
    "start": "node ."
  }
}

サーバー構成&やりたいこと

サーバーなど

  • サーバー -> now.sh(PasS)
  • DNS -> Cloudflare (プロキシはオン)
  • ビルド -> node.js server

やりたいこと

  • 基本的にはMySQLから動的にデータを持ってくる
  • フロント側は、挙動を軽くするために、データを細かく分けで受信する
  • now.shの無料枠に収めたい
  • 都道府県データなど、不変な部分をキャッシュさせたい

CloudflareのCDNにキャッシュしてるかの確認方法

ブラウザの開発ツールの「ネットワーク」タブで、受信したファイルをクリックしてレスポンスヘッダを見ればわかります。

  • 「cf-cache-status」が「HIT」 -> キャッシュできている
  • 「cf-cache-status」が「MISS」 -> キャッシュしてない
  • 「cf-cache-status」がそもそもない -> キャッシュしてない

キャッシュができているかをチェックするサイトもあるので、そちらを利用しても良いです。

CF-Cache-Status

やったこと

キャッシュされるまでにやったことをまとめます。時系列なので、意味のない部分もありますが、ご容赦ください。

CloudflareのPage Ruelで全キャッシュ -> ダメ

CloudflareのPage Ruleで、

*example.com/*

を「Cache Everything」として、全部をキャッシュする設定をしました。しかし、全くキャッシュされません。

調べるまで知らなかったのですが、Cloudflareはキャッシュさせるファイルの種類が決まっているそうです。

Which file extensions does Cloudflare cache for static content?

プロキシをオンにしたら、なんでもかんでもキャッシュすると思っていました。

上記の表の通り、画像やcss、jsなどの静的ファイルのみをキャッシュします。というよりかは、ファイルの末尾を見て、対象の拡張子になっているかを見ているっぽいです。

キャッシュさせたいクエリの末尾を.jsonにする ->ダメ

こちらは、Expressのrouteで設定します。

app.get('/prefecture/:id/detail.json' function(req, res, next) {
  // 何かの処理
  return res.send(何かのデータ)
});

当然ですが、キャッシュ対象ファイルになっていないので、キャッシュしてくれません。

CloudflareのPage Ruelで「*.json」を「Cache Everything」 -> ダメ

次に、CloudflareのPage Ruleで拡張子を指定しないとキャッシュしないという海外フォーラムを見かけたので、jsonファイルを全部キャッシュするルールを追加しましたが、それでもキャシュされませんでした。

途方にくれて見つけたこのページ

どうしようもないなぁと思っていたところ、Cloudflareのサポートページを見つけました。

Why is Always Online not working?

11個項目がありますが、9個目の、

You have caching headers coming from your server in the response (Cache-Control, Expires, etc.) that tell us not to cache the resource. This will cancel out most of Cloudflare's caching ability and Always Online.

これに心当たりがありました。

原因確定 -> ExpressのレスポンスヘッダがExpires= 0だった

ということで、確認したところ、ExpressのレスポインスヘッダがExpires= 0でしたので、上の9番目にバッチリマッチしました。

対策

都度headerを設定してもいいのですが、node.jsにぴったりのプラグインがあったので、そちらで対処しました。

インストール

node.jsでキャッシュコントロールをするなら、「express-cache-controller」が便利です。

express-cache-controller

npm、yarnでインストールします。

npm install express-cache-controller --save

アプリ側でロード

キャッシュコントロールをしたいところ(index.jsなど)で、プラグインをロードします。

const itemController = const cacheControl = require('express-cache-controller');

app.use(cacheControl({ maxAge: 0 }));

デフォルトは通常通り0にしておきました。

あとはレンスポンスを返す前に、「res.cacheControl」で設定をすればOKです。

const cacheControl = require('express-cache-controller');
app.use(cacheControl({ maxAge: 0 }));

app.get('/prefecture/:id/detail.json' function(req, res, next) {
  // 何かの処理 
  
  res.cacheControl = {
    maxAge: 15552000
  };  
  
  return res.send(何かのデータ)
});

Expressでキャッシュコントロールする「express-cache-controller」の使い方

Cloudflare側の設定

あとは、Cloudflare側で「.json」と付くものをキャッシュするPage Ruleを設定すればOKです。

*example.com/*.json

とすることで、すべてのデータでjsonファイルだけがキャッシュ対象になります。

ちなみに、ネット上では、「Cache-controllやExpiresのヘッダがあると、即キャッシュしない」という記事も見かけましたが、筆者がテストした限りでは、キャッシュ期間が未来であればキャッシュするようでした。


まとめ

ExpressのアプリでCloudflareのキャッシュを有効にするには、

  • プロキシ(DNSの雲マーク)をオンにする
  • キャッシュするルートには、専用のルールを設ける(今回なら、.jsonとつける)
  • Page Ruleに上記のルールを設定する
  • Express側でExpireの設定を多めに設定する(ここ重要)

ということでした。

わかってしまえば単純なことで10分もあれば解決できることですが、丸半日かかってしまいました。

でも、Cloudflareの挙動の理解が少し深まったのはよかったですね。