Download counter in PHP using .htaccess

Ever had this thought: “I want to fire something extra up when somebody downloads a certain file.”? This can be easily fixed by making all you download links link to a php page and output the download file for you. But what if the file is called directly in the browser, say Apache will happily give the file to the user, without me noticing it. Luckily for us, .htaccess is a great place to mess around with Apache.

This article will explain how to make pre-download conditions in php using .htaccess. I’ll make a download counter in this one.

Download counter in PHP using .htaccess

Download Counter in PHP using .htaccess

Think of the possibilities! You could build a download counter, a place to check if the user has rights to download a certain file, create a bandwidth limit, and the list goes on and on.

Keeping track of downloads is a feature many developers want, even firing “something” (read: doStuff()) before a file is requested for download is handy.

Okay, what do you need?

  • Apache web server with rewrite module enabled (probably the case)
  • a decent text editor (like notepad++)
  • optional: in this tutorial I’ll be using PHP

First, we need to get an .htaccess rule going. To do this you have to adjust your .htaccess file, or create a new one. To create a new .htaccess file in Notepad++ just open a new file and save as .htaccess. This is a hidden file on a unix system, but don’t worry.

Put the following lines in the .htaccess file:

#Let's do rewriting!
RewriteEngine on
RewriteRule ^(.*).(rar|zip|pdf)$ /download.php?file=$1.$2 [R,L]

This redirects every .rar, .zip and .pdf to the download.php file and include the file path as a parameter. I already hear developers think: "And what if I prompted it to output a .php file though a browser call?". We'll fix that in the PHP file.

To add more extensions, separate them with a | (shift + ).

A few notes on .htaccess:
The file will work from the root of the folder you'll put this into at your web server. All folders beneath the root will be affected. If you place the file in say /files/, the path will not be included in the file parameter. You'll have to either add this in the download.php file or .htaccess file.

Upload the file in your webroot.

Next up, we are going to create a table in MySQL. You can do this in phpMyAdmin, it's a small table.

If you already have a table called download, you have to change the SQL code.

CREATE TABLE `download` (
`filename` varchar(255) NOT NULL,
`stats` int(11) NOT NULL,
PRIMARY KEY (`filename`)

Execute this SQL statement in the SQL window, it'll create a new table.

Now it's time to get the PHP to work.

The following code allows you to keep track of download requests in the table you created above:


    //put connection to database here
    mysql_connect("localhost", "username", "password")
    or die ("Sorry, can't connect to database.");

    $filename = mysql_real_escape_string($_GET['file']);
    $path = $_SERVER['DOCUMENT_ROOT']."/"; //path of this file
    $fullPath = $path.$filename; //path to download file
    $filetypes = array("rar","zip","pdf");
    if (!in_array(substr($filename, -3), $filetypes)) {
        echo "Invalid download type.";

    if ($fd = fopen ($fullPath, "r")) {
        //add download stat
        $result = mysql_query("SELECT COUNT(*) AS countfile FROM download
        WHERE filename='" . $filename . "'");
        $data = mysql_fetch_array($result);
        $q = "";
        if ($data['countfile'] > 0) {
            $q = "UPDATE download SET stats = stats + 1 WHERE
            filename = '" . $filename . "'";
        } else {
            $q = "INSERT INTO download (filename, stats) VALUES
            ('" . $filename . "', 1)";
        $statresult = mysql_query($q);
        //the next part outputs the file
        $fsize = filesize($fullPath);
        $path_parts = pathinfo($fullPath);
        header("Content-type: application/octet-stream");
        header("Content-Disposition: filename=".$path_parts["basename"]."");
        header("Content-length: $fsize");
        header("Cache-control: private"); //use this to open files directly
        while(!feof($fd)) {
            $buffer = fread($fd, 2048);
            echo $buffer;
    fclose ($fd);


Let me explain what you can change in the code in short; in the first part of the file, you'll connect to your database. Please change the values to match your settings.

You can change the $filetypes to your likings, this will contain the accepted file extensions.

The php script will eventually output the file to your browser, neat eh? Download the zip file for a quick look in the files.

I hope this will help you to create pre-conditions on your downloads.

Good luck!

Liked this article? Sharing is caring!

34 Comments on this subject

  1. Tuan Anh said:

    Using .htaccess of this way has only rewrite-effect, and all other works are done by PHP. So that, when I read the title, I wondered why htaccess file can be used to delivered files? This post is the same as “PHP download manager”.

  2. Gaya said:

    First of all: thanks for all the comments!

    @Tuan Anh
    Maybe we interpret the title differently. I hope you still liked the article.

    Isn’t that a better to check user uploaded files?
    Because I uploaded the files on my server, I am sure I gave them the right extension.
    Or what did you mean?

  3. Kamen said:

    Well … actually one problem here is

    Content-type: application/octet-stream

    This is not working for my site, I would like to use the content-type that I entered in one of the Apache configuration files

    and another small problem – the extension is not always 3 character (I removed that code completely)

  4. Informatic said:

    Your code can be very unsecure. If you add “.php” extension to download-allowed-extension you can try download.php?file=../config.php for instance ;)

    Imho, it’s better to leave only “[L]” option in .htaccess and add code like this:
    # .htaccess:
    RewriteRule ^downloads/(.*)$ /download.php?file=$1&tkn=secretTok3n [L]

    # download.php:
    if ($_GET['tkn']!=’secretTok3n’) { die(‘Naughty boy, naughty…’); }

    $path = $_SERVER['DOCUMENT_ROOT'].”/downloads/”;
    $fullPath = $path.$filename; //path to download file

    if (strstr(realpath($fullPath), ‘/downloads/’) === FALSE) { die(’404 Not Found’); }
    // Here file can not exists (realpath returns FALSE if it’s not here) or someone moved “above” level of downloads. (hacking…)

    Also this function – – would be usable here (for example, if I want to download “txt” file, your script will force me to download it) :)

  5. Douglas said:

    Hi, I am running into errors with the php code. First error was on like 26, where you code says > which I figured out what a messed up >. However, I am stuck on the next error, which is line 40. The error is “Parse error: syntax error, unexpected ‘”‘ ”

    Any idea what I might be getting that? My line 40 is an exact copy of yours

    header(“Content-Disposition: filename=””.$path_parts["basename"].”””);

    Thanks, Douglas

  6. Ngamen said:

    Not work. This is appear in my screen “Invalid download type.” I am using this:
    $filename = mysql_real_escape_string($_GET['']);
    $path = $_SERVER[''].”/”; //path of this file

  7. me said:

    This won’t work with big download files, because whether MAX EXEC TIME will exceeded and/or it’s not resumable and not suitable for multiparts download slots as what every download manager does!
    so it’s only usable for small size files

  8. Bee said:

    Hello, i got error below:

    Notice: Undefined index: file in /home/xxxxx/public_html/counter.php on line 5 Invalid download type.

    Please help. thanks.

  9. moin said:

    if my download fails for internet interruption or power failure or something like this then I should not update database, in this case what should I do? I mean how can I track that my downlaod success or not?


  10. Janet said:

    hello there and thank you for your info – I have certainly picked up something
    new from right here. I did however expertise
    several technical points using this web site, since I experienced to reload the site
    lots of times previous to I could get it to load correctly.
    I had been wondering if your web host is OK? Not that I’m complaining, but slow loading instances times will often affect your placement in google and could damage your high-quality score if ads and marketing with Adwords. Anyway I’m adding this RSS to my e-mail and could look out for a
    lot more of your respective exciting content. Make sure you
    update this again very soon.

  11. Angel said:

    hi there, wot about if we want to send one time link to somebody, do you have any example with that, so far i know that can be witk a token. but i do not know how to do it.

  12. Vitamin A regulates the sex hormone progesterone,
    which is important when looking for the optimum in sexual health.
    Vitamin B helps to improve the blood circulation and stamina.

    Vitamin C helps to produce hormones needed for a healthy sex life and
    fertility. It helps to increase sperm count and their mobility, strengthens
    capillaries as well as veins and reduces blood cholesterol.
    Vitamin E promotes the creation of prostaglandins, hormones important to a healthy sex drive.

    Visit this website for more Buy Revatio Internet.

  13. Jonnie said:

    This is perfect, worked great I could see how many hits the file has on the database table on the stats tab, but I have a question, I want to know how can I add that same stats right beside the file name so people would know how many downloads that file has on public, URGENT!

  14. Enrico said:


    I tried the script very nice.
    However I have problem: it doesn’t work with file greater than 1MB..

    Reading previous comments seems people get mixed results about max file size.
    I guess it is related to some php variable… could you suggest which variable should I look at?

    I tried max_execution_time with no success


  15. Enrico said:

    Do not care about previous comment, the problem is elesewhere

    Everytime the file is downloaded by root folder.
    How can I change download path from root folder to a subfolder (e.g.: /downloadvalut/)?


  16. Rafael said:

    I’m having a little problem. Once I upload the .htaccess I get this error once I try to download the file..

    Warning: fopen(/var/chroot/home/content/27/11804327/html/Downloads/test.pdf) [function.fopen]: failed to open stream: No such file or directory in /home/content/27/11804327/html/website_net/download.php on line 19

    Warning: fclose() expects parameter 1 to be resource, boolean given in /home/content/27/11804327/html/website_net/download.php on line 48

    I double checked and the file is indeed there. Is there anything I did wrong??

  17. Alex said:

    I like it very much! Thank You Gaya!
    I don’t know almost nothing about MySQL and PHP, so i don’t know how to get info about download file. One php script with list of all downloaded files needed.
    Can You help me?

Leave your reply

Your email address will not be published. Avatars through Gravatar.


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>