発生した現象
ローカルPCに適当にサーバーを立て、html5のvideoタグで各ブラウザで表示する際、
なぜかSafariで動画が表示されない問題が発生しました。
しかも、まったく同じhtmlファイルでローカルPCのファイルパス直打ちであれば動画が表示されました。
上:ローカルPCのファイルパス指定
下:ローカルサーバーのURL指定
サーバーは php5.6を使って php -S localhost:8001
で適当に立てていました。
原因
Safariでは、mp4のようなサイズが大きくなると思われるファイルをHTTPリクエストする際に、
HTTP Range Requestを利用し、サーバーがこれに対応できないとそのファイルを読み込まないようです。
今回のHTTPリクエストでは、 Range: bytes=0-1
が指定されており、
「最初の1byteのみ送信せよ」というリクエストを送っています。
サーバーは「206 Partial Content」とともに動画データの最初の1byteを返すのが正解なのですが、
php5.6で立てたサーバーではこのRangeヘッダーに対応しておらず、
無視してmp4をすべて返してしまっていました。
Range RequestはRFC7233で定義され、2014年ごろに"Proposed standard"に昇格しました。
RFC 7233 Hypertext Transfer Protocol (HTTP/1.1): Range Requests
https://tools.ietf.org/html/rfc7233
RFC 7233 日本語訳
https://triple-underscore.github.io/RFC7233-ja.html
MDN Web docs
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
php5.6などの古いサーバーではこれに対応しておらず、
かつ、手元のブラウザ(Mac Safari/Mac Firefox/Mac Chrome/Windows10 Edge)を比較したところ
Mac SafariのみRange Requestに対応できない場合はエラーとなるため、
Safariのみ動画が再生できない不思議な挙動が発生していました。
いくつか手元にあった他のサーバーも試してみましたが、
-
php -S localhost:[port]
-> 🙅 (PHP5.6.3) -
python3 -m http.server
-> 🙅 (Python 3.6.5) -
ruby -run -e httpd .
-> ⭕ (Ruby 2.0.0) -
http-server .
(https://www.npmjs.com/package/http-server) -> ⭕ (0.11.1) -
static-html-server
(https://www.npmjs.com/package/static-html-server) -> 🙅 (0.1.2)
と、古くメンテされてないプロジェクトでは対応してないものがあるようです。
(コマンド例の一部は https://qiita.com/higuma/items/b23ca9d96dac49999ab9 を参考にしました)
Apache、nginxやS3、CloudFrontなどは当然対応しているので、開発環境以外で問題になることは少ないかと思いますが、
開発環境ではこの問題に引っかからないように注意しましょう。
その他
Safariが「mp4以外のファイル」でもRange Requestを必須にしているのかどうかは、ちゃんと調べてません。
どんな基準でRange Requestを使うようにしてるんですかね?
Golang(1.10.3)だと、適当に書いてもRangeに対応できる。
package main
import "net/http"
func main() {
panic(http.ListenAndServe(":8001", http.FileServer(http.Dir("."))))
}
go run httpServer.go
2010年ごろのauガラケーではRange Headerが必須だった時代があったようです。
当時対応するのは結構大変だったんじゃないですかね……
http://tnamao.hatenablog.com/entry/20100617/p1
備考
検証に使用したHTMLファイル
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="http://vjs.zencdn.net/7.0/video-js.min.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/7.0/video.min.js"></script>
</head>
<body>
<video class="video-js vjs-default-skin" controls muted autoplay>
<source src="./google.mp4" type='video/mp4' />
</video>
</body>
</html>
追記
2016年時点では状況が異なり、
SafariではRangeヘッダーがついていなかったんだそうです。
(しかもその当時は、Firefoxでは206 Partial Contentを返すと動画が途中までしか再生されない問題が発生したらしい)
(情報提供: https://qiita.com/jmatsu )
追記(2021-06-28)
Twitterで指摘がありましたが、UnityのVideoPlayerからのリクエストもHTTP Range Request対応が必要らしいです。
(情報提供: https://twitter.com/t_furu/status/1409257855622938629 )
また、2021年現在でもこの記事へのLGTM/ストック数が増え続けているので、Range Requestがないと動作しないブラウザは現在もいくつか存在するものと思われます。(再検証したいがめんどくさい)