I have a contact form that, if used after a certain time, will throw a "This form has expired. Please refresh and try again." error.

I have added the following to my contact form, thinking it would solve the problem:

<input type="hidden" name="XID" value="{XID_HASH}">

It didn't.

Another solution I have come across is to set $config['disable_csrf_protection'] = "y"; (per: http://ageekandhisblog.com/expressionengine-how-to-fix-this-form-has-expired-please-refresh-and-try-again/). However, I'm concerned there might be security issues associated with this solution.


  • What version of ExpressionEngine are you on? Commented May 12, 2015 at 4:13
  • v2.9.2 - but I can upgrade. Commented May 18, 2015 at 19:47
  • Bradus' answer below is correct that XID is deprecated, but has not been removed, so it should still work in 2.9.2. Is there a proxy in front of the web server? Have you viewed the rendered source to see what is being put into your input? Commented May 19, 2015 at 12:51
  • Is the issue that if the page is left open long enough, you get the expired form error? Or is it just always giving that error, even if you fill out the form right away and submit it?
    – jrothafer
    Commented May 28, 2015 at 19:44

6 Answers 6


I've looked through the source code for you but I'm drawing a blank. I thought there was an arbitrery timeout value in the token however in 2.9.2 this seems to have been depreciated (or I imagined it)!

I'm also going to assume the error is not a missing XID/CSRF_token as this has been covered above and no answer is forth-coming.

First Question : How are you creating your form? If you could provide your code that would be great. Is it a Freeform, a Channel Entries form or your own custom one??

Anyway, to help you debug, when you create your form, if you use EE tags or the php ee()->functions->form_declaration() a CSRF token should be generated (it's actually generated super early in the request but for simple example), this will be added to the hidden fields of your form that form_declaration/tags print out to the template. In the back what happens is that this token is added to the exp_security_hashes table so it can be referenced. Assuming you're using Cookie based (as opposed to Session ID based).

Debug point 1 : When you create/display your form is the correct entry being added to the DB table?

Question 2 : Are you using standard Cookie based sessions? You can see this in the CP under Admin > Security and Privacy > Security and Sessions > Website Session Type.

So, now we've debugged the token setting, lets look at the submission. One clue from your OP is

I have a [..] form that, if used after a certain time, will throw a [...] error.

This is what made me skim through to see if there is an expiry. There is not (from my scan, I'm not infallable!). The first thing I note is that if you are using cookie based authentication there is a hard coded expiry on the token! In file expressionengine/libraries/csrf/Cookie.php line 42 you'll see this :

public function get_expiration()
    return 60 * 60 * 2; // 2 hours

That's a 2 hour expiry on a cookie based session right there, this could cause invalid CSRF's.

Debug Point 2 : If you are using Session ID based security on the front end then are you ensuring that the S parameter is passed along with the form submission, either as a cookie or in the URL string? If you submit a form without an S parameter it can't match the users session and can't check the token.

So finally, now we assume your using session ID based authentication. we can see from the file expressionengine/libraries/csrf/Database.php line 35 :

public function get_expiration()
    return 0; // never - times out with session

This means the token is valid as long as the users session is valid. So we now have to check the session settings... Now, the session handling was changed in 2.8.0, however we can get some clues from the file system/expressionengine/libraries/Session.php (This file is well worth reading just becuase it explains the session types at the top in comments!!), on lines 73 and 74:

public $user_session_len    = 7200;  // User sessions expire in two hours
public $cpan_session_len    = 3600;  // Admin sessions expire in one hour

That's a 2 hour timeout on users using the front end in Sesion ID mode. It's also worth checking your cookie domain in both cases, and if you're posting from http to https or vice versa.

Question 3: How long in terms of inactivity does the form take to expire? 2 hours??

Debug Point 3: Try altering the cookie/session timeout values on your local site, see if it holds the form 'open' for longer/shorter. If you find that this is what is causing your timeouts, there's a load of modules (or you can core hackz it yourself) out there for you to better control the session.

I do hope the above helps you out a bit. Of course if this (and none of the other answers) does not solve it for you then all this typings been for nothing! (hehe) but please do come back to us (via comments, or edit your OP) with your further findings.

If one of the answers did help you solve it please do mark the answer, alternatively if you get to the bottom of it please "Answer your own Question" so we can all bask in the glorious solution!


if you have v 2.10 - try to replace

<input type="hidden" name="XID" value="{XID_HASH}">


<input type="hidden" name="csrf_token" value="{CSRF_TOKEN}">

The XID constant is deprecated starting from 2.8.0

  • I'm on v2.9.2. If I upgrade, will the problem be resolved? Commented May 18, 2015 at 19:47
  • No go. I changed to {CSRF_TOKEN} but still get the same error. I'm still on v2.9.2. Commented May 27, 2015 at 1:43
  • 1
    Can concur that csfr_token won't work, but xid_hash will. This on EE2.10.1 Commented Sep 20, 2015 at 17:05
  • Does not resolve it, get the same error with EE v2.11.7
    – BNetz
    Commented Mar 16, 2017 at 10:02

There's also another possibility that I ran into with the "This form has expired. Please refresh and try again." error.

Using Stash, I recently turned on caching on my pages that include forms. The issue here is that the the CSRF token from a previous user is cached in this scenario. This created the error on every page including a submitted form. It was a "duh" moment for me, but I thought it would be worth relating in case anyone else runs into this issue.

You want to make sure you wrap your forms in stash:nocache - https://github.com/croxton/Stash/wiki/%7Bexp:stash:cache%7D:

{exp:stash:cache bundle="my_cache_bundle"}

{!-- this will be cached --}
{exp:channel:entries channel="my_channel"}

{!-- this will be escaped --}
    My Form


Usually this error occurs when you are posting anything without adding csrf_token or xid.

for the newer versions of expresionengine xid became csrf_token.

show you need to put a hidden field inside your form tags as follows.

<input type="hidden" name="csrf_token" value="{CSRF_TOKEN}" />

If you are using xid and your expressionengine is newer than 2.7.0 replace as above.

  • Using {CSRF_TOKEN} did not resolve the issue for me. I'm still getting the same error. Commented May 27, 2015 at 1:42
  • I just tried 2.10.1 with XID I am not getting an error and with {CSRF_TOKEN} I consistenly get an error - using a Profile:edit login form. Just extra data! Commented May 27, 2015 at 2:03
  • (The XID is replaced properly when I look at the source, the {CSRF_TOKEN} is not - however it appears profile edit is adding it anyway: <input type="hidden" name="csrf_token" value="00303e28e0e367095c2a7..etc" /> - Commented May 27, 2015 at 2:06

Are you blocking cookies? If so, allow cookies and try to submit your form again.


I was having this issue on ExpressionEngine 2.10 and up. I found my redirect wasn't working in the .htaccess file. Once I fixed the redirect from www.site.com to site.com all was well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.