How to mix public and private folders in a Drupal installation | Creating a private folder in Drupal

| | 3 min read

By default, Drupal serves all its files publicly via http. Any one can directly access the files through a browser by entering its path. This is also true for files which have permissions for them. Unauthorized users can bypass Drupal by copying and pasting the link in the address bar.

Obviously this can can pose a security issue. The solution provided by the Drupal core is  to make the entire file-system private. To implement it we have to either block direct access to the files directory via .htaccess or move the files directory outside the public html folder.

This method will inevitably make every file accessed via Drupal including the images and attachments in the content inaccessible. During normal web browsing, the browser will have to make a separate http request for each and every content  to the server. 

If we make the entire file-system private, each time the browser makes a request it is routed through Drupal. This will lead to the scenario where Drupal is booted every time a request is made as all these requests are to be made via Drupal. This is very expensive and resource intensive especially if there are multiple images and content in the page and may eventually bring down the server.

In a common scenario, most of us only need to set access restrictions to files attached to a specific node, or files uploaded to a specific folder. The private file system implementation provided by Drupal does not allow this directly, but we can achieve this by using the Drupal file_download function and a .htaccess redirection.

So here is the situation we are currently facing:

We want to restrict access to those files attached to a particular node to only those who are able to access that node and those who have the permission to view the uploaded files.

Here is the solution:

Configure the node privacy by role or content access module to restrict access to the nodes. If you are using cck file field, set the proper permissions.

Set upload path module so that the files you want to restrict will automatically go to a specific directory inside the files directory. Say  for example sites/default/files/privatedownloads (you can create any number of subdirectories inside that and the access restrictions are applied to those also.). For filefield cck, there is a option to set the custom directory, so this module is not required.

Add an .htaccess file to the sites/default/files/privatedownloads with the following content

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteBase /system/files/privatedownloads
  RewriteRule ^(.*)$ $1 [L,R=301]

What we are doing is to create a permanent redirect to all files in this directory to the Drupals system/files menu (/system/files/privatedownloads)

Thats it!! The rest will be taken care of automatically by Drupal.

Alternatively you can also use the following modules to reach the same solution.

private_download is a module that implements the above change.

private_upload is a module that allows
uploading files to both private and public directories at same time.(this works with only the upload module)

Note: I found a problem with the file_download function which gives a 404  error when trying to download filenames with a "&" in it. The clean solution would be to use traslitration module, which will sanitize the filenames when it is uploaded.

If you want more info about this issue, then check out Drupal and Drupalcoder.