NuxtデフォルトサーバはCache-Controlできないので注意
まとめ
- Nuxtは各種のサーバサイドフレームワークと組み合わせられるがNuxtだけでもサーバとして機能する
- デフォルトで ETag は吐くが Cache-Control は吐かないのでそのまま CDN に食わせるといつまでもリフレッシュされない
- この影響を受けるのはあくまで vue-router を経由する Page だけ
- assets に関しては nuxt start で serve している場合は問答無用で max-age は 1 year になる1
- optional な http response header を追加する middleware はどうもなさげ
- 他のフレームワークと組み合わせてその middleware を使えば追加できる(Koa で確認済み)
そもそもWebのcacheって
- private cache と public cache がある
- CDN は public cache
- ブラウザは private cache
- Cache-Control を private にすると CDN は cache できない(したがって、リクエスト数も転送量も減らない)
- とにかく Nuxt.js は何の情報も追加しない
※ public と private の話は恥ずかしながら CDN切り替え作業における、Web版メルカリの個人情報流出の原因につきまして - Mercari Engineering Blog にあるようにメルカリのやらかしで知りました。(ちなみに Rails はデフォルトで max-age=0, private, must-revalidate を付加するので特に何も考える必要なく CDN はスルーになる。)
CDN側では基本的にmax-ageを推奨してる
- CloudFront エッジキャッシュにオブジェクトを保持する時間の指定(有効期限切れ) - Amazon CloudFront
- チュートリアル: キャッシュ制御 - チュートリアル | Fastly Help Guides
基本的には Cache-Control: max-age= を設定するのがよさげ。(Surrogate-Control や s-maxage だったりもしますが、細かいことは置いておく。)
しかし Nuxt.js にはサーバ側の設定の余地がない。(SSR用の MartinLG/nuxt-custom-headers: Nuxt module to add custom headers to SSR rendered pages. というものはある。)
NuxtではETagと304しか考えてない
具体的なコードは以下の部分。
lib/core/middleware/nuxt.js
// Add ETag header
if (!error && this.options.render.etag) {
const etag = generateETag(html, this.options.render.etag)
if (fresh(req.headers, { etag })) {
res.statusCode = 304
res.end()
return
}
res.setHeader('ETag', etag)
}
nuxt.config.js の render.etag option を見るこの辺以外に cache を考慮しているコードは存在しない。ETag を付けるか付けないかだけ。直接ブラウザとやりとりするならこれだけでも十分かもしれないが CDN を通す場合はそうはいかない。
そこでNode.jsのサーバサイドフレームワークと組み合わせる
nuxt-community/create-nuxt-app: Create Nuxt.js App in seconds.
を使えば Express, Koa, Hapi, … などなどからフレームワークを選べる。これらの middleware で custom header を吐いてあげればおk
具体的に効果的にcacheするには
例えば Koa であれば
- Koa - next generation web framework for node.js
- koajs/ctx-cache-control: Augment Koa with ctx.cacheControl(maxAge)
を組み合わせて、./server/index.js で
const Koa = require('koa')
const app = new Koa()
require('koa-ctx-cache-control')(app)
app.use(async (ctx, next) => {
ctx.cacheControl('max-age=0')
こんな感じ。
こうすると pages 以下の component を serve する時には Cache-Control: max-age=0 が付加されたうえで ETag でのキャッシュ内容のチェックが行われるので、origin サーバから直接転送されるものはコンテンツの鮮度チェックはしつつも転送はほぼエッヂサーバから行われるので速い。(はず)
cache buster の効くファイルが生成されている ↩