Home » Wordpress » Protecting / serving a file via readfile(), force download?

Protecting / serving a file via readfile(), force download?

Posted by: admin November 30, 2017 Leave a comment


I’m trying to make a simple script that does two things:

  1. Serves up a file and hide’s it’s destination
  2. Has a download counter

Now, I’m doing this in the wordpress environment, but this question isn’t completely wordpress-related so I figured I would ask here.

Basically, the way I have it set up, currently, is I have a link that when you click it sets a $_['GET'] which is then checked if is set. If it is set, the download file is served.

the link: <a href="http://localhost:8888/fresh/?dl_id=01">Click here!</a>

the $_['GET'] code: http://pastebin.com/93nD43gA

There is a bit of wordpress jargon in the code, but basically it’s checking a download count user_meta and if it’s > 0, serveFile() is called.

The main problem I’m having here is, if I click the link, readfile() loads the actual file contents INTO the window (garbled text). If I add a target=_blank to the <a> it opens a new browser window and loads the contents INTO the window.

This approach seemed to work perfectly fine when I was doing it as stand-alone php files. My main issue is that I need to keep the wordpress space so I can call functions, etc. associated with it.

I have tried using the $_['GET'] on both the self page, another page with a custom template (the code in the pastebin above), and as a stand-alone php file. Both the first two options load the file INTO the window. The third doesn’t preserve wordpress functions, even if I include blog-header.php.

Can anyone point me in to the right direction of how to get the file to force download and not load INTO the window?


You need to set the appropriate header for whatever the file type is. For example, if readfile always serves, PDFs, it should be done like this:

// disable browser caching -- the server may be doing this on its own
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header('Content-Type: application/pdf');
//forces a download
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=filename.pdf');

Keep in mind that header only works if you have not sent any data in the request at all including whitespace.


The ‘garbled’ text is what you want however besides that you have to set a mime. This can be accomplished by simply setting a header, e.g. header("Content-Type: image/png");

If the file mimes will vary (e.g. pdf, doc, png, etc) you should look into finfo extension. With it you can get the full and correct mime of the file

$finfo = new \finfo(FILEINFO_MIME);
$mime = $finfo->file('path/to/file', FILEINFO_MIME_TYPE);
header("Content-Type: $mime");

As noted – headers can be set only if no write to output has been done (no echo’s, print, etc. Output buffering could help you here).