Password Protected Content Made Simple
by Erik Reagan
Every so often I come across a project where part of the spec involves a private area of content accessible via a password. ExpressionEngine gives us a number of ways to protect content out of the box.
One of the basic approaches is to restrict content to member groups thus requiring a member account for each person. If your content is not member-specific another approach is to create a generic user account and supply each individual with the username and password with which to log in.
Each approach would work in numerous scenarios. However, today I want to look at how we can utilize weblogs on a very basic level to manage password protected content very easily. Our goal is to result in a process that uses default EE functionality and that the “average client” can manage with little difficulty or confusion.
We will be using a single weblog to manage the password-protected content. The type of content is irrelevant at this point so let’s focus on how we can achieve this. At the end I’ll show you a working example of this technique.
How do we store the password?
Considering that we don’t need a deep level of security we only have a few requirements to meet. The most obvious is that no two entries can have the same password. Since we are working “out of the box” with ExpressionEngine we are limited to how we do this. The approach we will take is to use the URL Title field to store the password. EE never stores two URL Titles with the same string so that makes it a perfect way to store our unique password.
Next, we need a way for the user to submit the password to query the database. We will use the standard weblog entries tag pair to do this inside a template running some simple PHP.
So what does this all look like?
The first thing we will need is a weblog, a custom field group and a template. We will work off of the name Private Content so let’s create the following:
Weblog:
- Name: Private Content
- Short Name: private_content
Custom Field Group:
- Name: Private Content fields
- 1 field called “private_content” set to textarea
Template Group:
- Name: “private”
- Templates: index is all we need with PHP on input and no caching
Now that we have created our puzzle pieces go to your weblog groups and assign the custom field to the weblog. Before we dive into templating let’s just add a few entries, so we have something to work with as we add the code to our template.
Go to Publish > Private Content and create an entry with the title “Private Content One” and the URL Title (which is our password) of “password1”. In our content textarea we can just put something simple like “Did this work?”. Feel free to create a few additional entries if you’d like to test them as well. Now that we have some password protected content in the database, we can look at our template.
Our Template Code
Our template code is actually quite concise. We have enabled PHP in our template and set it to input because we will want to use some PHP to determine what to show our users.
Be sure that caching is turned off. Due to the nature of this approach you could potentially cache the private content and we don’t want that.
Let’s first look at the entire template and then go step-by-step.
<h2>Private Content</h2>
<?php
global $IN;
if ( ! $the_password = $IN->GBL('the_password', 'POST') ) :
?>
<form action="" method="post">
<p>
Enter your password:<br/>
<input type="password" name="the_password" value="" id="the_password" />
<input type="submit" name="submit" value="Access Content →" id="submit" />
</p>
</form>
<?php else : ?>
{exp:weblog:entries weblog="private_content"
url_title="<?php echo $the_password;?>"
dynamic="off" limit="1"}
{if no_results}<p>Your password is incorrect. Would you like to
<a href="{path='private'}">try again</a>?</p>{/if}
{private_content}
{/exp:weblog:entries}
<?php endif; ?>
Let’s look at this in two pieces: PHP and ExpressionEngine tags. Here’s the PHP without anything else distracting us:
<?php
global $IN;
if ( ! $the_password = $IN->GBL('the_password', 'POST') ) :
?>
// our form
<?php else : ?>
// EE weblog entries tag pair
<?php endif; ?>
The first thing we have to do with PHP is globalize the EE Input class. This gives us access to some of the code that ExpressionEngine provides us, thus reducing the amount of work we have to do. In this case we will be using a function (GBL()) to retrieve a POST variable.
(Now you may be asking yourself, “why would I use a function if I can just get the variable with $_POST[‘variable’]?” Well I’m glad you asked! The GBL() function in ExpressionEngine does a bit of house cleaning for us and first checks if the variable is set and has a value. If it is not set then the function returns FALSE which is very useful to us.)
Moving on to the next line we begin our conditional statement. We are trying to set the variable $the_password to the POST value of the respective input. If it has not been set yet then it will return FALSE. So in plain English the conditional says “if the password has not been set then show our form, otherwise run our ExpressionEngine weblog entries tag pair.”
ExpressionEngine code
Now let’s look at the EE code being used to retrieve our entry. We have a few things going on here so here’s the weblog entries tag pair isolated.
{exp:weblog:entries weblog="private_content"
url_title="<?php echo $the_password;?>"
dynamic="off" limit="1"}
{if no_results}
<p>Your password is incorrect. Would you like to <a href="{path='private'}">try again</a>?</p>
{/if}
{private_content}
{/exp:weblog:entries}
A quick look will tell you that we are specifying the url_title directly in the entries tag and setting dynamic to off. This is key to returning the correct result. If dynamic is not set to off then anyone can use the URL to access the private content. We also use the no_results conditional to show the user an error message with a link to try again. If a result is returned then that means that the password the user entered was correct and it will display for them.
A note on security
You may have already realized this, but this approach is not for elements or content that you really need to lock down tightly. The url_title field is stored in the database as plain text so anyone with database access can retrieve this data. I’ve found this approach to be nice for “private” blog posts and similar use while not requiring someone to register on your site (thus using the member_group restriction approach mentioned earlier). Perhaps some add-on developer reading this will be inspired to write a custom fieldtype that is for password storing and actually encrypts the data. Only time will tell.
Taking this one step further
With the use of custom fields this approach can provide quite a nice offering to your site. Consider using FF Matrix and LinkLocker to provide private content for downloads via a single password for the entry. Or perhaps you could have a rich-content article that is private and managed with a WYSIWYG add-on like Wygwam or LG TinyMCE. Using a hint of creativity with this could allow for some pretty neat solutions.
You can try out this method by visiting my example implementation: Password Protected Content Demo.
Post to Twitter
Post to Delicious

Pat Brumfield — 07:24 on 04.13.2010
thanks for this thorough look at setting up password-protected content. i could have used this a few months ago, but instead just built my own little php-based solution. this will definitely come in useful in the future. cheers.
Garrett Winder — 10:33 on 04.13.2010
Just used this on a current project and, needless to say, it works perfectly. Thanks for the great post!
Erik Reagan — 10:37 on 04.13.2010
Thanks Pat and Garrett. I’m glad you’ve found use in this
cmac — 18:56 on 04.13.2010
Thank you for this. Just today we were trying to figure out the best solution to provide simple password protected content on a per entry level rather than having to create a new member group or weblog. GREAT tip!
hcabbos — 15:02 on 04.14.2010
Awesome article! The demo isn’t case sensitive, though. It’s supposed to be according to the response text.
Erik Reagan — 02:16 on 04.15.2010
@cmac
Thanks! Glad you found it useful.
@hcabbos
Thanks for pointing out the case (in)sensitivity.
Elliot — 07:08 on 05.12.2010
$IN->GBL(
no longer seems to work in EE 2.x? Do you know how to access the same thing in EE 2.0.2? Thanks
Erik Reagan — 09:20 on 05.12.2010
Hey Elliot
For EE2.x you would use something like this I believe
$this->EE->input->post('the_password');Michael Hughes — 23:42 on 11.02.2010
Hi Erik,
Thanks for the great article. Has it been determined if this does work in EE 2? I’ve tried to implement the code that is listed in the entry above but I’ve had no luck. Any insight would be greatly appreciated.
Thanks
3 Roads Media — 01:51 on 11.03.2010
Brilliant post—just what I needed. The fix for EE2 is swapping out:
if ( ! $the_password = $IN->GBL(‘the_password’, ‘POST’) ) :
with:
$the_password = $this->EE->input->post(‘the_password’);if ( ! $the_password ) :
Michael Hughes — 02:34 on 11.03.2010
Thanks! That worked perfectly!
dr3w — 19:21 on 12.07.2010
i tried, but could not make it work, using EE, tried the changes above and it seemed to work, but after hitting submit, it comes back to the same page. no content or wrong password does anything…
any advise?
also, do i need to change weblog to channel in the code above?
Erik Reagan — 16:11 on 12.08.2010
@dr3w
Yes, for EE 2.x you need to change “weblog” to “channel”
dr3w — 16:22 on 12.08.2010
Erik,
thanks. I have done that too, but I still am unable to get any results from it. I have turned php on for the template, set to input,.. but no matter what, all i see is the box for entering the info… and no matter if i try the right or wrong password, brings me back to the same page. what can I be missing/done wrong? any idea? thanks again, sorry for the trouble…
Erik Reagan — 16:23 on 12.08.2010
Tough to say. Why don’t you try to catch me on AIM or Skype sometime and I can take a look.
dr3w — 07:49 on 12.11.2010
Erik,
Thanks again for your very kind offer. After speaking with you, we determined that the problem was with cutting and pasting the code, where the “‘s came wrong… thus, after fixing it, it worked like a charm.
Thanks again,
Erik Reagan — 07:50 on 12.11.2010
Glad to help!
Jessica — 13:16 on 02.07.2011
Hi Erik,
Thank you for sharing this! Where in the above example(s) do I set/replace the desired password? Also, how would you assign more than one password?
Thanks a ton!
Erik Reagan — 14:52 on 02.07.2011
Hi Jessica
The password is set in the URL Title field. Due to the nature of that field, you cannot store multiple passwords for a single entry with this method.
Erik
Jessica — 15:45 on 02.07.2011
Hi Erik,
Thank you for your quick response
Are there any issues in using this method with Structure?
I’m using EE 2.x with Structure. I’ve updated the code for 2.x compatibility as written in the above comments (and fixed the “s); however nothing happens when I enter the password/url title, or when I enter an incorrect password.
This is what I have in my template:
<?php
global $IN;
$the_password = $this->EE->input->post(‘POST’);
if ( ! $the_password ) :
?>
...
<form action=”” method=“post”>
<label>Enter your password:</label>
<input type=“password” name=“the_password” value=”” id=“the_password” /></p>
<input type=“submit” name=“submit” value=”” id=“submit” /></p>
</form>
<?php else : ?>
exp:channel:entries channel=”{staticchannel}” channelslug=”{staticchannelslug}” url_title=”<?php echo $the_password;?>” dynamic=“off” limit=“1” entry_id=”{entry_id}” disable=“categories|category_fields|member_data|pagination|trackbacks”}
...
{if no_results}Your password is incorrect. Would you like to
try again?{/if}
{/exp:channel:entries}
<?php endif; ?>
And here is the page:
http://www.dev.desertsunresort.com/the-resort/homeowners/
Thanks again for all of your help!
Erik Reagan — 15:47 on 02.07.2011
In EE 2.x the dynamic tag changed. Now instead of
dynamic="off"you use
dynamic="no"See if that change does the magic.
Erik Reagan — 15:49 on 02.07.2011
Jessica,
I also noticed you had an ‘entry_id’ parameter in your tag. You should remove that. I’m not sure if it would cause a problem but it’s not needed in this scenario since the url_title is being pulled from the _POST data.
Jessica — 16:11 on 02.07.2011
Hi Erik,
Thanks again for being so quick to help out! I changed the dynamic parameter (silly me) to the new 2.x setting, and also removed the entry_id parameter… still isn’t working :(
I’ll keep trying and I’ll let you know if/what I figure out!
If you have any other ideas please let me know
Daniel — 18:24 on 03.13.2011
Hey Eric,
thank you dude. I was looking for more information on that issue and honestly its working really well for my private content. Thank you again.
Regards
Jon Bishop — 22:35 on 03.27.2011
Fantastic post! Is there a way to display multiple entries?
Thanks!
Erik Reagan — 10:45 on 03.28.2011
Daniel,
Glad you found this useful!
Jon,
Since we’re using the url_title to specify an entry there’s no direct way to use multiple entries.
I suppose you could use a playa field to map multiple entries to a single password-specific entry. That creativity is up to you though
Best,
Erik
Jon Bishop — 13:46 on 03.28.2011
Thanks Erik! By the way, the email I received about your comment being posted gave me the link below to come back to this post:http://eeinsider:8888/index.php/password-protected-content-made-simple/