UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Safari】no-store がついていてもbfcacheが有効になる

以前キャッシュが残っていたためCache-Control: no-storeをヘッダに設定する対応を行った

uga-box.hatenablog.com

しかし、Safariではbfcacheという訪問したページ間の戻る、進むという動作にページのロードをおこなわない機能がCache-Control: no-storeを設定していても効いてしまうらしい

少し古いがSafariのリリースノートに記載されている
Release Notes - Safari Technology Preview - Safari - Apple Developer

Allowed pages served over HTTPS with Cache-Control: no-store header to enter the back-forward cache

実際に挙動をみてみる

問題になった構成に近づけるために以下の構成にした

  • expressでAPIサーバを用意
  • APIのレスポンスヘッダにCache-Control: no-storeを設定
  • vueでコンテンツをつくる
  • vueのcomputedでfetchを使ってexpressのAPIからデータを取得してvueのdataを更新する
  • 2ページ用意し、最初のページに次ページのリンクをつけておく

APIサーバ

'use strict';

// [START gae_node_request_example]
const cors = require('cors')
const express = require('express');

const app = express();

app.get('/api/message/', cors(), (req, res) => {
  const now = new Date();
  res.header('Cache-Control', ['no-store'].join(','));
  res.json({
    message: `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`,
  });
});
// Start the server
const PORT = process.env.PORT || 4040;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="app">
    {{ message }}
  </div>
  <a href="/next.html">next</a>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello First Page! - '
    },
    created() {
      const _this = this;
      fetch('http://localhost:4040/api/message')
        .then(function(response) {return response.json();})
        .then(function(data) {
          _this.message = _this.message + data.message;
        })
    }
  })
</script>
</html>

next.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="app">
    {{ message }}
  </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Hello Next Page! - '
    },
    created() {
      const _this = this;
      fetch('http://localhost:4040/api/message')
        .then(function(response) {return response.json();})
        .then(function(data) {
          _this.message = _this.message + data.message;
        })
    }
  })
</script>
</html>

表示されたページ
f:id:uggds:20200621235912p:plain:w300

変化がわかりやすいように時刻を表示するようにした

Chrome と Safar で比較

操作手順

  1. First Pageを表示 → nextをクリック
  2. Next Pageを表示 → ブラウザバック
  3. First Pageを表示

Chrome
f:id:uggds:20200622011200p:plain

Safari
f:id:uggds:20200622002005p:plain

確かにSafariCache-Control: no-storeを設定していてもブラウザバックをするとデータが更新されていない模様

対策

pageshowイベントとpersistedを使う

https://developer.mozilla.org/ja/docs/Web/API/Window/pageshow_event
https://developer.mozilla.org/ja/docs/Web/API/PageTransitionEvent/persisted

persistedはWebページがキャッシュからロードされているかどうかの真偽値プロパティ

ただ、これをキャッシュさせたくないページ全てにいれないといけないのは悩ましい