Select Page

Serving Secure Files With CFContent Tag’s File Attribute In ColdFusion

Published: December 9, 2022

The other day, when considering which files live inside the wwwroot folder on the ColdFusion server, I mentioned that “secure files” live outside the wwwroot folder; but, can be made accessible to the user via ColdFusion. There are many ways to do this; but, perhaps the easiest way is to use the CFContent tag’s file attribute. This attribute allows any physical file on your server to be sent to the user, regardless of where it lives.

For this demo, imagine that our ColdFusion application has a path mapping to a non-public folder where all of our secure files are stored:


Since these files live outside the wwwroot folder, the user cannot access them directly. However, when the user provides a correct password, we’re going to dynamically serve one of the secure files to the user via the CFContent tag. Additionally, we’re going to use the CFHeader tag / Content-Disposition to “change the name” of file as we stream it. This way, we can have a database-driven filename on the server while still providing a user-friendly filename for the download.

	// Setup form value defaults.
	param name="form.submitted" type="boolean" default=false;
	param name="form.password" type="string" default="";
	// If the form has been submitted and the password is correct, send the file to the
	// user via the CFContent tag.
	if ( form.submitted && ! compare( form.password, "letmein" ) ) {
		// The name of our super secret secure file stored in a non-public folder. We're
		// going to use the CFContent tag to stream this non-public file to the user.
		secureFilename = "0112c97f582d546e39d3547631ef6e85.jpg";
		// The user-friendly name of the file as it will appear when downloaded.
		clientFilename = "lucy.jpg";
			name = "Content-Disposition",
			value = "attachment; filename=""#clientFilename#""; filename*=UTF-8''#urlEncodedFormat( clientFilename )#"
			type = "image/jpeg",
			file = expandPath( "/secure-files/#secureFilename#" )
<!--- ------------------------------------------------------------------------------ --->
<!--- ------------------------------------------------------------------------------ --->
<!doctype html>
<html lang="en">
	<meta charset="utf-8" />
		Please enter password to download secure file
	<form method="post">
		<input type="hidden" name="submitted" value="true" />
		<input type="text" name="password" value="" />
		<button type="submit">
			Download file

In this case, the CFHeader tag is actually more complicated than it needs to be for the demo. I have it setup to serve filenames with extended UTF-8 characters, even though my secure file only has simple ASCII characters in it.

But, the CFHeader tag is only adjusting the filename being presented to the user – the CFContent tag is doing the real work; its file attribute points to our non-public /secure-files directory, where it is dynamically streaming the private file to our user. As such, when the user enters the correct password, they have the following experience:

A non-public file being downloaded by a user once the user has entered the correct password in a web form.

As you can see, the non-public file is streamed to the user with a new filename.

ASIDE: In this demo we’re keeping the secure file in place since it’s not user-specific. However, if we were dynamically generating a file on-the-fly and wanted to delete it after ColdFusion was done streaming it, we could include the deleteFile attribute on the CFContent tag.

The file attribute is only one way for the CFContent tag to stream data – it also provides the variable attribute for streaming any binary value to the user. In fact, if you look at my previous post on proxying Gravatar images through ColdFusion for better caching, I use both the file attribute and the variable attribute in order server up avatar images from different sources.

The CFContent tag is just one of those ColdFusion features that makes web application development with CFML really convenient.

Want to use code from this post?
Check out the license.