mabots' blog

知のレバレッジを最大化せよ (旧はてなダイアリーから移転しました。)

iOS の Safari で Rails のページをリロードすると真っ白画面になる件

iPhone でリロードを多用した操作を Rails アプリケーションに対して実施すると、真っ白いページが表示されることがあります。今回 iOS 7 の Safari だけでなく、OS X 10.8 の Safari でも再現したので、まとめてみました。

状況

画面としては真っ白画面が表示される。Safari の debugger でみると、head body の中身がない。

ステータスコードとしては、304 Not Modifided として取り扱われている。

Webrick の log をみると、サーバとしては 200 を返しているので、Last-Modified を比較して safari が処理していると推測される。

Started GET "/" for IP_ADDR at 2014-08-29 11:23:05 +0900
Processing by CertainController#CertainAction as HTML
  Rendered hoge.html.slim (0.1ms)
  (snip)
Completed 200 OK in 111ms (Views: xx ms | ActiveRecord: yy ms)

原因

Erlingur.is *1 によると

It seems that for some reason Safari 7 will cache the 304 blank response body for a page and then return it on subsequent requests. This seems like a bug in Safari 7 on OS X and on iOS 7.

Safari 7 になってから 304 の status が帰ってきた時に真っ白ページをキャッシュして、その後ずっとその真っ白ページが表示される。iOS 及び OS X に搭載された Safari 7 のバグと思われれる。」とあります。

一時的な対応

  • header の last modified を常に更新されたことにして 304 として取り扱わないような対応がありますが、とても dirty なやり方かつパフォーマンスにも影響するので到底薦められるものではありません。一応下記を application controller の before action に追加する方法で再現しなくなっていることは確認しました。
headers['Last-Modified'] = Time.now.httpdate

VGTech *2では、

To do this, we created a Varnish rule which removes the If-Modified-Since and If-None-Match headers on request and then have Varnish deliver the content as normal. This rule only applies to iOS7-devices, since this is where we know the problem exists.

「If-Modified-Since と If-None-Match headers を iOS 7 だったらその場合に限定して取り除く」としていますが、厳密には、OS XSafari でも起こるのでこのあたりの条件は吟味したほうがよいでしょう