Whenever you upload a file to your content folder and then access this file via its URL in the browser, a copy of the file is created in the media folder for public access. Additionally, the Panel creates thumbs for display in sections and fields or in the file view. This approach has several advantages, one of which is that you can move your content folder out of the web root for an additional layer of security.
However, if we want to restrict access to certain pages of a site as explained in our Restricting access to your site recipe, any files attached to these pages will still be accessible to anyone who can guess their URL.
In this recipe, we will create a simple
downloads page, where registered users can download files. The example will be based on Kirby's Starterkit.
- A fresh Kirby Starterkit running on your local computer
- A code editor
- Apache web server (or other web server with a configuration that makes sure that requests to the content folder are sent to Kirby's router)
For the new downloads page, we create a new page blueprint. It can have any content your like, but for our purposes, all we want is a files section:
All files we upload to this section get a template
protected assigned to it that we can later use to filter files or create conditions.
Now modify the
pages section in the
site.yml file so that it looks like this:
We add the
downloads template to the allowed templates and remove the existing
create: default option.
Now open the Panel and create a new page with the
downloads template (or create it manually in the file system if you prefer). Then upload some files (images and other) to this newly created page via the files section.
Make sure to upload the files via the Panel so that the
protected template is assigned to the files. If you just put some files into the downloads page manually, you would also have to manually add the metadata file with the template information.
We also want a
downloads.php template to list the uploaded files.
When you now visit this page in the frontend and inspect the file URLs in your browser's dev tools, you can see how these point to the media folder.
And if you click on the links while watching the media folder, you can see how it fills up with copies of those files.
Let's prevent that.
/site/plugins/ folder (create this folder if it doesn't exist), create a new folder
files-firewall and add the obligatory
index.php file inside it.
index.php file, add the component like this:
Inside the component we check the file's template, and if it has the
protected template assigned, we change the URL of the file to a location of our liking, otherwise we return the default
If you delete the media folder now, and click on the file links again, you will see that this time no file copies will be generated anymore.
However, the files will still be accessible via their standard URL (in this case
http://localhost/downloads/filename.ext). A route to the rescue.
Let's add a route to the plugin that sends users trying to access
downloads/filename.ext to the error page:
With this route in place—and if our web server is an Apache server using our default
.htaccess—our file is now fully protected, because a rewrite rule in our
.htaccess file prevents direct access to the
In other environments, where calls to files in the
/content folder are not routed through Kirby's router or where the original rules from the Starterkit's
.htaccess file have not been copied 1:1, files might still be accessible via their direct file path, e.g.
http://localhost/content/downloads/myfile.pdf. So make sure that your configure your server correctly.
If you now you click on a file link, the file will no longer be accessible to anyone, and we can start opening up our configuration to authorized users.
We can now adapt our route so that logged-in users would be able to download the files, while all other visitors are sent to the error page:
Of course, instead of allowing access to logged-in users, your conditions here can be different. You could, for example, check if users send a specific token or if they are authorized in some other way.
The above already works very well for non-image files. However, the Panel automatically creates previews and thumbs of image files which will end up in the media folder despite our efforts from above.
The Kirby component responsible for thumbs is the
File::version component. Let's modify this component in our plugin:
Again we check the file template and return the original file if the file has the
protected template, otherwise return the original component with its options.
To finish this up, here is the complete code again:
With a few lines of code, we were able to set up a basic files firewall where authorized users can download files while access to these file is prohibited for all other users.
As always, there are many ways to refine this basic setup with more sophisticated conditions or make it configurable via the Panel, but you now know where to start from. Happy coding!