php readfile('test.mp4') command not working only on safari


macrumors 65816
Original poster
Sep 11, 2008
Does anyone know how to fix this? I have googled and some people say safari is funny about headers other say its to do with partial content, but no working solutions.

The following code is a super simple example it is the only code on the php page and the mp4 file is in the same directory. In every browser except Safari the video appears on the page.


header('Content-Type: video/mp4');
header('Accept-Ranges: bytes');
header("Accept-Ranges: 0-filesize( $file )");



macrumors 6502a
Oct 29, 2008
I remember solving this in Node.js a while ago, and you basically need to return a correct Range response for Safari, to understand you are trying to return a video content.

Unfortunately I can't provide you a working example in PHP, but this could guide you to a working solution:

Basically you need to listen to Safari (read it from the request headers - Range) for a range of bytes which it requests to be played and calculate a size of a chunk to be returned in your response.

Below you can find a cut/paste part of the Node.js implementation which could give you a bit more info how should you proceed:

 const stats = await fileStats(fn);

        const range = req.headers.range || "";
        const total = stats.size;

        if (range) { // if range header in the request exists it's Safari
            let start, end;

            const [partialstart, partialend] = range.replace(/bytes=/, "").split("-"); 

            start = parseInt(partialstart, 10);
            end = partialend ? parseInt(partialend, 10) : total - 1;

            const chunksize = (end - start) + 1; // size of a chunk we return back based on partial start & partial end

            res.writeHead(206, { // http code response + headers for Safari
                "Content-Range": "bytes " + start + "-" + end + "/" + total,
                "Accept-Ranges": "bytes",
                "Content-Length": chunksize,
                "Content-Type": contentType
            createReadStream(fn, { start, end }).pipe(res);
  • Like
Reactions: whitedragon101