1

do you know if it's possible to make a file only available for download by clicking a link in code, and restrict access by trying to access the file from copy/pasting the URL in your address bar?

Example:

I generate invoices as PDF, and I want to make them available only for logged in customers and only the invoices that are generated for this specific customer. Customer A is not allowed to see the invoices from Customer B and vice versa.

Now I save these invoices here: www.mydomain.com/invoices/

and an invoice for Customer A is something like: www.mydomain.com/invoice/20130202_120045.pdf

now I want to make it possible that a customer can only see this PDF when I add a link like this in HTML:

<a href="http://www.mydomain.com/invoice/20130202_120045.pdf">See invoice</a>

but the PDF should NOT be accessible by typing in directly www.mydomain.com/invoice/20130202_120045.pdf in the address bar.

is this in some way possible ?

Bobrovsky
  • 13,789
  • 19
  • 80
  • 130
Danny Hobo
  • 574
  • 1
  • 5
  • 24
  • See also: http://stackoverflow.com/questions/10596116/caching-http-responses-when-they-are-dynamically-created-by-php/10596231#10596231 - it explains how this can easily be done without incurring PHP overhead by using `X-SendFile` (family) – Ja͢ck Feb 05 '13 at 10:39

3 Answers3

4

There are multiple approaches, but I'd say this is the easiest one:

  • Store the actual PDF in a directory only accessible by PHP (outside your web root or blocked by .htaccess for example).
  • Verify access in the PHP script.
  • Let the download happen through a PHP script using the header Content-disposition to set the filename and using readfile() to output the file contents.
Mario
  • 35,726
  • 5
  • 62
  • 78
2

Sure - the easiest way would be to use .htaccess. You can store all your files that shouldn't be downloadable via URL in one folder, then redirect all of them to a PHP script.

This way, when a person tries to download http://www.mydomain.com/invoice/20130202_120045.pdf, they will be (in the background, via .htaccess) redirected to a PHP script somewhere.

That PHP script can then verify if the person is authorized (via session, cookies...) and only output the original file if they are (or error message if they're not).

You can output the file in parts using fgets() function to make sure that you don't overload memory on your server.

A disadvantage of this approach might be scalability, since the more files your users download, the more PHP scripts need to be ran and could overload Apache at some point. But that would be unlikely if the code is written well (i.e. using fgets() etc.)

Zathrus Writer
  • 4,311
  • 5
  • 27
  • 50
0

Using mod_xsendfile this is pretty easy. You stick the files outside of the document root and issue a special header that tells Apache to send the file on your behalf:

After setting it up, the code is as simple as this:

<?php
...
if ($user->isLoggedIn())
{
    header("X-Sendfile: $path_to_somefile");
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$somefile\"");
    exit;
}
?>
<h1>Permission denied</h1>
<p>Login first!</p>

Source code is taken from the module page itself.

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309