Using the Private File System In Drupal 8
The primary purpose of any content management system is to allow administrators to create content without having to have extensive experience with HTML or even an understanding of how a web server works, Drupal is no different. In today's online world, however, the definition of content has expanded far beyond text on the screen. Users want pictures, manuals, documentation, and audio and video content.
But what do you do if you want to restrict access to certain files from general public consumption? How can you hide your files from search engines?
Enter the private file system.
Drupal has had the concept of public and private file systems for awhile now and they all work pretty much the same way. Public files are just that ... publicly accessible from any web browser by typing in the URL for a given file. Private files, however, are a bit more complex.
How it works.
The private file system in Drupal masks the real URL for a given file under a system path. When the file is requested, Drupal then translates that URL into the physical file path on the system, then reads the file into memory and serves it to the web browser.
This allows you to have basic (very basic) restrictions to files. When the content item that the file or image is attached to is published, users will be able to see the image or view/download the file. If you need to have finer grained control over the files, you will have to either write a custom content access module yourself, or use one of the ones already provided by the Drupal community.
Understanding a web server file system
Before configuring Drupal to use the private file system, you need to first understand how web server file systems work in general. Each website has a document root directory; where all of the files for the website reside. Every file and directory (subdirectory) under this root directory is accessible from any web browser by typing in the URL. By default, Drupal stores the files and images uploaded to the website in the subdirectory sites/default/files under the document root.
The first thing that you need to do in configuring Drupal to use the private file system is to decide where you want your private files stored. A lot of sites that I've been asked to work on have typically used the subdirectory sites/default/files/private.
While Drupal will allow this configuration, I don't condone or recommend using any path under the web root directory because, in theory, the URL's to files under that subdirectory could be guessed and anyone could access the files by typing in the URL to a file in their web browser. A better location for files that are to be managed by the private file system is outside of the web root directory. I typically create a directory at the same level as the web root directory named “private_file_system” (both on the web server and in my local development environment).
Configuring Drupal for private file storage
In Drupal 8, the private file system must be configured in the settings.php file. This is a change from previous versions of Drupal where you could set the file paths from the file system configuration screen.
After you have created the directory that will be used for storing your private files, open the settings.php file for your website and locate the
$settings['private_file_path'] variable. It will most likely be commented out and look like the following:
... /** * Private file path: * * A local file system path where private files will be stored. This directory * must be absolute, outside of the Drupal installation directory and not * accessible over the web. * * Note: Caches need to be cleared when this value is changed to make the * private:// stream wrapper available to the system. * * See https://www.drupal.org/documentation/modules/file for more information * about securing private files. */ # $settings['file_private_path'] = ''; ...
Remove the hash symbol ( # ) from the beginning of the line. Then in between the quotes after the equal sign, type or paste in the full path to the directory that you created for your private files. For example, my settings.php file contains the following:
... /** * Private file path: * * A local file system path where private files will be stored. This directory * must be absolute, outside of the Drupal installation directory and not * accessible over the web. * * Note: Caches need to be cleared when this value is changed to make the * private:// stream wrapper available to the system. * * See https://www.drupal.org/documentation/modules/file for more information * about securing private files. */ $settings['file_private_path'] = '/home/webuser/private_files'; ...
Once you have made the change, save the file and upload it to your web server. After uploading the file, you will need to clear the caches on your site for the setting to take affect. You can do this either by going to Administration › Configuration › Development › Performance or if you are using Drush, run the Drush command cache-rebuild (or use the command alias, cr).
You can verify that your private file system is correctly set up and ready for use by looking at the file system configuration if there is an available option under Default download method for Private local files served by Drupal. If you select this option and save the form, when creating new fields for your content types, the default settings for the field will be to use the private file system. I do not recommend this for performance reasons.
Using the private file system for fields
Now that your private file system is configured, it's time to start securing your super secret files! To demonstrate how to use the private file system, I'll be adding a file field to the default basic page content type in my local development sandbox site.
Browse to the content type configuration page by going to Administration › Structure › Content types and click on the Manage fields button under operations. From the manage fields screen, click the Add field button at the top of the screen.
We'll be creating a private file field, so from the Add new field drop-down and select File. Enter a label for the new field, I'm calling my new field Attachments. Click the Save and continue button. This next screen is where the rubber hits the road!
Now you get to configure a few options for the new field, but the one we are most interested in is the Upload destination. Here you will see options for Public and Private files and should select Private. (You can set the other options however you see fit.) Click the Save field button to save the configuration.
The important thing to remember here is that if you plan on sharing this field with any other content types, they too will use the private file system. This may not be desirable if I also wanted to create an 'attachments' field for say, forum posts.
The next screen lets you configure options for the field used on the Basic page content type. Set any options that you wish here, the private file system has no affect on these settings. My settings are shown below. Your new file field is now configured to use the private file system and ready for use.
Demonstrating the new private file field
Now, to demonstrate how the private file system affects files uploaded in Drupal, you'll want to create a new basic page. Navigate to Content and click the Add content button at the top of the page then select Basic page. The content form now has the new Attachments field so I'm going to upload a sample PDF file and publish the page.
After publishing the content, you can now click on the link for the uploaded file and it will either display in the browser window or start to download to your computer depending on what the file type was that you configured while setting up your field.
If you view the source and look at the link for the file, you will see that the path to the file is something like http://www.example.com/system/files/my-awesome-file.pdf. This is not an actual file path under the web root directory, but a mask that points Drupal to where the actual file is stored internally. Now the system can read the file contents into memory and serve it to the user like any other file. You can copy and paste that URL in an incognito or private browser window and still be able to access the file.
So how's this different than the public file system then?? Good question. To demonstrate how it's different, edit the page and unpublish it. Now, open a new private or incognito window in your browser and paste the URL for the file in the address bar and hit enter.
Booya! Access denied!
In a nutshell, you get the access denied page is because the anonymous user isn't allowed to view unpublished content and the file is part of that content. Under the hood, Drupal translates the file URL and sees that it's a file using the private file system and therefore, since it's parent content is unpublished and inaccessible, then the file is also inaccessible.
As you can see, out of the box, this functionality is very basic and to get the most use out of it, you'll need a custom or community content access module. In a future post, I'll show you how to create a simple custom content access module to only allow a file to be downloaded if a user subscribes to your email list!