PHP: Hide the Real File URL and Provide Download via a PHP Script

by Yang Yang on May 18, 2009

There are times when you need to store a file (such as one that you sell for profit) outside of the document root of your domain and let the buyers download it via a PHP script so as to hide the real path, web address or URL to that file. Use of this approach enables you to:

  1. Check for permissions first before rendering the file download thus protecting it from being downloaded by unprivileged visitors.
  2. Store the file outside of the web document directory of that domain – a good practice in web security in protecting sensitive and important data.
  3. Count the number of downloads and collect other useful download statistics.

Now the actual tip. Given that you have put the file to be downloaded via the PHP script in place at /home/someuser/products/data.tar.gz, write a PHP file with the following content in it and put it in the web document directory where your site visitors can access:

$path = '/home/someuser/products/data.tar.gz'; // the file made available for download via this PHP file
$mm_type="application/octet-stream"; // modify accordingly to the file type of $path, but in most cases no need to do so

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($path)) );
header('Content-Disposition: attachment; filename="'.basename($path).'"');
header("Content-Transfer-Encoding: binary\n");

readfile($path); // outputs the content of the file

exit();

Now your site visitors can and can only download the protected file via the PHP script.

Subscribe to Kavoir: blog feed

You should also read:

Randall Ijams July 12, 2009 at 6:51 am

First, thanks.
Second, FYI, only relitive paths work on my local apache HTTP server. I can not use ‘/Downloads/test.txt’, I must use ‘../../Downloads/text.txt’. I have yet to get it to work on my real server but I’ll keep trying.

Rob in Charleston, SC October 7, 2009 at 8:34 am

This works like a charm. Thanks very much.

Rui October 15, 2009 at 3:04 am

It’s not working for me…

I’ve tried to use it with ‘pdf’ and ‘doc’ files but it doesn’t work.

I even changed the Content-Type to ‘application/pdf’ and ‘application/msword’ but it still doesn’t work.

What it seems to be happening is that it only downloads 1KB of the files.

For the PDF file for example (456KB) when I try to open I get a message saying that the file is damaged.

Any ideas of what may be wrong?

Here’s the script I’m using:

1) get_file.php:

2) index.php:

has a link that calls get_file.php:

Click here to get the file

Thanks!

Yang Yang January 26, 2010 at 1:39 pm

Make sure there’s no extra unwanted characters in the get_file.php such as hard-to-notice white spaces.

Rui October 15, 2009 at 3:51 am

Ok, my bad.

The path was incorrect. The script is working fine now.

Thanks a lot!!!

Yang Yang October 17, 2009 at 7:09 pm

You are welcome, Rui! :)

Yang Yang January 26, 2010 at 1:38 pm

Awesome news!

Bryan Ierardi November 15, 2009 at 3:52 am

Thanks for sharing this great information. This was helpful to me in learning about how to initiate a file download while protecting the URL of content only to be accessed by authenticated users. Keep up the great work.

Stefano December 2, 2009 at 6:34 am

Hi your tutorial seems great unfortunatly for me a I have little knoledge of php so I don’t understand how to make it work for me. Can somebody be so kind to explain with some example
sorry and thank you for your time
Stefano

tejas March 19, 2010 at 10:04 pm

Hi,
I made a php script from your code, but I can still see the download URL on the page, and so can download the files as well. Do I have to call this script from my download page? I have a thank-you page with the download url.

Thanks,
-tejas

Ron March 26, 2010 at 8:26 am

The code seems to be working but instead of the file downloading I am getting gibberish in the window. I’ve tried to change the MIME types but nothing seems to be working. The weird thing is that it worked fine the very first time I used it but not since. Does anyone have any ideas?

Thanks.

dbphydb April 15, 2010 at 2:53 pm

Hi im new to all this.
I need to automate the process of downloading and installing an exe file abc.exe from say ‘http://10.34.45.21:8080/cruisecontrol/artifacts/xxx_trunk_nightly_build/xxx/test/’

I mean now i have to manually goto ”http://10.34.45.21:8080/cruisecontrol’ then click on each folder before i finally click on the abc.exe file. Then it downloads the exe on my machine. Then i have to double click on the exe to install it. I want to automate this whole process such that when i run the script it will automatically download the exe file and install it. Is it possible to do this using php?

Yang Yang April 19, 2010 at 4:01 pm

I’m afraid not, or it’d be a major security breach for all the browsers. You can automate the downloading just by giving the full URL address to the file to your site visitors or friends. No need to click through all the directories. However, you can’t make it automatically run on people’s computer after downloading.

David May 26, 2010 at 6:34 am

I know nothing about PHP, but am looking for a way to allow visitors to part of my site to download an ebook in epub format. Your script is the closest I’ve come to a solution and I thank you for it. However, when the download is completed, the downloaded file receives an additional extension: instead of simply being ROE.epub (the name of the file on the server), it is downloaded as ROE.epub.html.

Any idea what is wrong? (Could it be that the $mm_type has to be set to a different value than “application/octet-stream”?)

Michael November 21, 2010 at 7:02 am

I think I’m missing something. Isn’t the download file itself (at the Real File URL) on the server and viewable by anyone? So copying it to a new location, then downloading, doesn’t change the fact that the orignal file is on the server and viewable by anyone.

Yang Yang November 22, 2010 at 9:34 pm
Sohaib September 1, 2012 at 1:03 am

There is a folder on server that is publically accessible usually public_html or www but for this to work you should store file in nonpublic folders. Non public folders are not viewable by anyone except scripts/owner. or you can do as ‘Yang Yang’ says

xavi November 30, 2010 at 9:31 pm

Hi!

Just wanna tell that this script worked so well for me! I only needed to add some previous filtering code and that’s it.

I’ve also added your blog to my delicious list :) CU soon!

David December 29, 2010 at 3:33 pm

Excellent script!

Here’s an interesting question, that probably has a super simple answer, but I do appreciate you putting up with me.

I’m trying to “download,”/upload a file dynamically to my server, preferably with Php but anything will do. I don’t have access to the actual file name, just a link, such as you created with this script.

For example, I’m trying to get a video from youtube on to my server. This link will give me the download, but I need to get that file directly to my server instead of my computer first:
http://bit.ly/eqo2AM

Any ideas, or could you point me in the right direction? Thank you so much for your help! Get me runnin’ and I’ll definitely link to you as a “consultant”!

David December 30, 2010 at 9:39 am

Oops, never mind. Just found a script that’ll do the trick for me. Thanks!

vikas January 1, 2011 at 5:20 pm

just wanted to know that if the file is on someone else’s website, then if i use this script, will my bandwitdh be used?

vikas January 1, 2011 at 5:25 pm

will this use my bandwidth or of the server where the file is located?

Rohan Dhamal February 25, 2011 at 10:19 pm

Hi,
when I echo the image contents, then it adds 2 white spaces at the start, and thus image gets corrupted..

Why is that ?

Jim S. Smith January 15, 2013 at 8:08 am

If you are using “echo” for image data, yes – it will corrupt the data.

For image data, which is binary not text, you should use an “image” statement instead.
imagepng – for PNG images,
imagejpg – for JPG images,
imagegif – for GIF images.

Also, don’t forget to use the “image_destroy” statement when you are done.

Mizan April 14, 2011 at 1:57 pm

good one, do you have a solution for bigger files? I don’t want to load the whole file to memory at once, say it’s a big like 300MB file. How do i do that?
thanks.

Blackhawkso April 16, 2011 at 9:26 pm

Hey dude
Great script but have two very quick questions about this script.

1. How can i send the user to a page after the download has either started or finished.

2. How can i get the script to tell my database that the file has been downloaded.

Thanks ffor your time.

Shelley July 4, 2011 at 10:45 am

Will not work with Internet Explorer 9. Will download a empty 0 byte file

Yasantha October 8, 2011 at 10:49 pm

Thanks. It’s really useful.

Pranay November 10, 2011 at 9:12 pm

This script works fine.

If you have a page one.php refering to the download.php to download the file it works fine. Though you can only keep continuing at page one.php after the download is done.

The page is pausing untill the download is completed how can you avoid this? How can you keep navigating on the one.php?

Antonio February 22, 2012 at 3:18 am

A thousand thanks!

It took me a while to realize that there should be no line break in the code. It was driving me crazy!

Also, this is the link that must be placed in your website to access the download:

file.extesion

Also, for newbies like me this can also be useful:
place this at the beginning, in a new first line
[/blockquote>

Again, thanks a lot!!

Antonio February 22, 2012 at 3:28 am

Hi again,

Let me ask a question.
If i have a thousand files i cannot have the same thousand download.php files but say filename1.php, filename2.php…

Do you know a way of batch creating the download.phps from the name of the files in a folder?

Or is there a way that the download.php, from the href of the link, knows what file is being asked?

Thank u!

Robbie April 2, 2013 at 10:23 pm

Actually, it’s relatively simple.

Change the

$path = ‘home/someuser/products/data.tar.gz’;

line to

$path = ‘home/someuser/products/’ . $_GET['filename'];

Then, when you provide a link to the download file, name it as

<a href=”download.php?filename=FILENAMEGOESHERE”>download</a>

Understand?

Tony February 24, 2012 at 12:36 pm

Hi,

I am using this script to stream videos from one server to another without the user being able to see the full URL of the videos but now my web host is telling me that their tmp directory is constantly being filled up. What would cause this to happen and what can I do to fix it?

any help would be appreciated

Eric April 17, 2012 at 10:31 am

I am able to get this script to work perfectly when downloading files through a browser on a PC. However, when I try to download a file using this script on a my smartphone (Galaxy Nexus) I download an empty file with the correct name and extension. Do you have an idea why this would happen on a mobile phone/browser?

David June 20, 2012 at 9:51 am

This is excellent simple yet easy to use.

Thank you for sharing.

Emma Davis August 21, 2012 at 7:12 pm

Thank you for this, it has worked perfectly for a pdf download I was trying to sort out.

Mark December 18, 2012 at 1:29 pm

Just a tip, you might want to add an ‘if file_exists’ to it so that in case of a mistake in the path, no PHP file in which the secret information is being exposed will be downloaded.

Just put this around the header/readfile/exit:

if (file_exists($path)) {
}

Of course, you can add an else-element to, redirecting to another page or echoing something like ‘Access Denied’ or ‘Doesn’t work’.

Jim S. Smith January 15, 2013 at 8:02 am

Pretty good trick!

The only problem I have, is that some hosting services (especially for free hosting accounts) do not allow you to store anything above your document root directory.

I think a better fix for this kind of limitation would be to create your special folder that actually stores the downloadables, and have its permissions marked “x0700″, and have them accessible only through your PHP modules on the server. Another way, would be to rename these files to “dot-named” files if you have that ability with your account. Somelike this example: “saysomething.pdf” => “.saysomething”. Then, you can have PHP read it as normal, but send it as MIME-type “PDF” to the visitor. This can also be used as a means to protect your image files from being scarfed up be harvester-bots, etc. Just as long as you make sure that your PHP module knows the proper MIME-type to send it as.

Other than that, really great tip. I was looking for a means to do this as a form of “anti-leeching”, together with the proper entries in the site’s “.htaccess” file!

Steven March 28, 2013 at 9:51 pm

I Have to anonymise a url…how to do that ? the file is hosted on another folders of my server !

Cynthia June 26, 2013 at 9:28 pm

Awesome! Thanks for the script. Now I have a page of downloads that are href linked. Would love to figure out how to issue the script in the file just created when the image/text is clicked.

Comments on this entry are closed.

{ 2 trackbacks }

Previous post:

Next post: