readfile

Reads a file and writes it straight to the output buffer, returning the number of bytes sent.

Signature
readfile(string $filename, bool $use_include_path = false, ?resource $context = null): int|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$filenamestringPath or stream URL of the file to output.
$use_include_pathoptionalbool= falseWhen true, the file is also looked for along the include_path.
$contextoptional?resource= nullA stream context resource created with stream_context_create().

Return value

int|falseThe number of bytes read and written on success, or false on failure.

Errors

Emits an E_WARNING and returns false when the file cannot be opened.

readfile() opens a file, copies it to the output stream, and closes it — in one call, without building a PHP string of the contents. That last point is the whole reason it exists: a 2 GB video goes out through a small internal buffer instead of blowing past memory_limit.

When to reach for it

Use it in a download endpoint that serves files from outside the document root after an authorisation check. Send the headers first — Content-Type, Content-Length: filesize($path), and Content-Disposition: attachment; filename="..." — then call readfile() and nothing else. It is also handy for inlining a static HTML fragment that must not be parsed as PHP.

What trips people up

Output buffering silently defeats the memory advantage. If output_buffering is on, or a framework has called ob_start(), the entire file accumulates in the buffer anyway. Call ob_end_flush() (or while (ob_get_level()) ob_end_clean();) before readfile() in a download handler.

Also: any byte your script emits before the call — a stray newline after ?>, a BOM, a warning — is prepended to the file and corrupts the download. And never pass a user-supplied path straight in; resolve it with realpath() and confirm it sits inside your intended directory, or you have handed out arbitrary file read.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Serve a private PDF or invoice from outside the web root only after checking the session.
  • Stream a large video or backup archive without exhausting memory_limit.
  • Emit a cached HTML fragment verbatim instead of including it as PHP.

Frequently asked questions

What is the difference between readfile and file_get_contents?

readfile() writes the file directly to output and returns a byte count; file_get_contents() returns the contents as a string you can manipulate. Use readfile() for downloads because it never holds the whole file in memory.

Why is my file download corrupted when using readfile?

Almost always because something was output before the call — whitespace after a closing ?> tag, a UTF-8 BOM, or a PHP warning — and those bytes get prepended to the file. Remove trailing ?> tags and clear the output buffers before sending.

Does readfile use a lot of memory for large files?

Not on its own; it streams through a small internal buffer. It does if output buffering is enabled, because the buffer then accumulates the entire file, so close the buffers first.

More examples

A guarded download endpoint

realpath() collapses any ../ in the request, and the str_starts_with() check confirms the result is still inside storage/ — together they stop path traversal. Clearing the buffers first is what keeps memory flat for a large file.

Example · php
<?php
$base = realpath('storage');
$path = realpath('storage/' . ($_GET['f'] ?? ''));

if ($path === false || !str_starts_with($path, $base . DIRECTORY_SEPARATOR)) {
    http_response_code(404); exit;
}

header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($path));
header('Content-Disposition: attachment; filename="' . basename($path) . '"');
while (ob_get_level()) { ob_end_clean(); }
readfile($path);

Capturing readfile output

The buffer captures what readfile() printed, showing that readfile() writes to output rather than returning content. If you actually want the string, file_get_contents() does it in one step and without the buffer dance.

Example · php
<?php
file_put_contents('data.csv', "a,b\n1,2\n");

ob_start();
readfile('data.csv');
$contents = ob_get_clean();

echo strtoupper($contents);
echo "Use file_get_contents() when you need the string, not the output.\n";

unlink('data.csv');

Serving a byte range

readfile() always sends the whole file from byte zero, so HTTP Range requests need fopen(), fseek() and fpassthru() instead. The printed count is the remaining byte total after the seek.

Example · php
<?php
file_put_contents('big.bin', str_repeat('ABCDEFGHIJ', 10));

// readfile() cannot skip - use fopen/fseek/fpassthru for ranges
$fh = fopen('big.bin', 'rb');
fseek($fh, 20);
echo fpassthru($fh), " bytes from offset 20\n";
fclose($fh);

unlink('big.bin');

Discussion

  • Be the first to comment on this lesson.