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 X の Safari でも起こるのでこのあたりの条件は吟味したほうがよいでしょう