Installing WP Super Cache with lighttpd

19/02/2009

Trying to get WP Super Cache & Wordpress working on my fast lighttpd server, I came into problems, mainly because of lighttpd’s lack of (Apache’s version of) the mod_rewrite module. The static files that were created from the cache were not statically served from wordpress. The problem is that in order to use them, the PHP fcgi was called for each request. So, why would we have to call PHP every time that a file can be completely statically provided by the web server?

Following this guideI came up with some problems trying to serve the static files. The problem with that version of the rewrite.lua script is that it does not really work the way it should. The whole point of using WP Super Cache is to avoid calling the PHP fcgi for posts that are already cached into an html file. Calling the PHP fcgi is much slower than using the “core” lighttpd static-page-serving facilities.

So, what did I do to avoid calling the PHP fcgi?

The following script takes the url that was asked from the client. It checks whether there is a fresh version of a static HTML page on the cache and if yes, it servers that. If the file does not exist al all or the it is expired(I check its modification date) then the request is forwarded to the PHP fcgi so that it can be freshly served.

In my lighttpd.conf I have put this:

</p><p style="clear: both"> </p><p style="clear: both">$HTTP["host"] == "www.asteriosk.gr" {</p><p style="clear: both"> </p><pre style="clear: both"><code>alias.url = ( "/storage/" => "/opt/storage/" )
    server.document-root = "/opt/apps/wordpress/"
url.rewrite = (
    "^/(wiki|wp-admin|wp-includes|wp-content|storage)/(.*)" => "$0",
    "^/(sitemap.xml|sitemap.xml.gz)" => "$0",
    "^/(.*.php)" => "$0",
    "^/(.*)$" => "/index.php/$1"
)
magnet.attract-physical-path-to = ( server.document-root + "rewrite.lua" )
</code></pre><p style="clear: both"> </p><p style="clear: both">} 

And in the rewrite.lua file I have put this:


expiration_time = 10*60

function serve_html(cached_page, expiration_time)
  attr = lighty.stat(cached_page)
  --Check if the cached file has expired
  if (attr and (attr['st_mtime'] + expiration_time) > os.time() ) then
    lighty.env["physical.path"] = cached_page
    return true
  else
    return false
  end
end

function serve_gzip(cached_page, expiration_time)
  attr = lighty.stat(cached_page .. ".gz")
  --Check if the gziped cached file has expired
  if (attr and  (attr['st_mtime'] + expiration_time) > os.time() ) then
    lighty.header["Content-Encoding"] = "gzip"
    lighty.header["Content-Type"] = ""
    lighty.env["physical.path"] = cached_page .. ".gz"
    return true
  else
    return false
  end
end

attr = lighty.stat(lighty.env["physical.path"])
if (not attr) then

  lighty.env["physical.rel-path"] = lighty.env["uri.path"]
  --Change the "/opt/apps/wordpress/" to your own wordpress location
  lighty.env["physical.path"] = "/opt/apps/wordpress/"
                                 .. lighty.env["physical.rel-path"]
  -- If we are querying, we don't have to cache of course
  query_condition = not ( lighty.env["uri.query"] and
                          string.find(lighty.env["uri.query"], ".*s=.*"))
  --If there exists a cookie in the client, probably he/she has been here before
  --and has left a comment. In that case we don't use cached content
  --for example, the user might has just submitted a comment.
  user_cookie = lighty.request["Cookie"] or "no_cookie_here"
  cookie_condition = not (string.find(user_cookie, ".*comment_author.*") or
                          string.find(user_cookie, ".*wordpress.*") or
                          string.find(user_cookie, ".*wp-postpass_.*"))

 if (query_condition and cookie_condition) then
    --construct the full path of the expeted  cached filename for this url
    accept_encoding = lighty.request["Accept-Encoding"] or "no_acceptance"
    cached_page     = lighty.env["physical.doc-root"] ..
                           "/wp-content/cache/supercache/" ..
                           lighty.request["Host"] ..
                           lighty.env["request.uri"] ..
                           "/index.html"

    cached_page = string.gsub(cached_page, "index.php/", "/")
    cached_page = string.gsub(cached_page, "//", "/")

    --If the client accepts gzipped content, send gzipped content
    if (string.find(accept_encoding, "gzip")) then
      --If for some reason the gzipped file does not exist, fallback to the
      --uncompressed cached file
      if not serve_gzip(cached_page, expiration_time) then
      serve_html(cached_page,expiration_time) end
    else
      serve_html(cached_page,expiration_time)
    end
  end
end

If you want to use the script in your own server, the only things that you have to change is the hardcoded /opt/apps/wordpress/ path and the expiration_time variable.

Kudos to Giovanni Intini for porting the Apache modrewrite’s rules on modmagnet and the original idea of the script.


There are 15 comments in this article:

  1. 3/07/2009marty baratz say:

    Great work

  2. 3/07/2009rodrigo duran say:

    thank you dear friend,

  3. 3/07/2009KATHY COMER say:

    Hi can someone please translate for me thanx,

  4. 3/07/2009Aaron Bremer say:

    Thanks this design is very good..,

  5. 3/07/2009tai truong say:

    Good One!,

  6. 1/08/2009Lighttpd and WP Super Cache « The Zash Blag say:

    [...] would you need lua scripting to serve cached static files when you can symlink everything in wp-content/cache/supercache/*blog.hostname*/ to the wordpress [...]

  7. 2/08/2009PHP Trivandrum » Wordpress Super Cache on lighttpd say:

    [...] Read the rest of his version Installing WP Super Cache with lighttpd [...]

  8. 24/08/2009Wordpress Lighttpd vs. Apache Httpd Perfomance Test « Web Server Hacks Blog – Hacks for Dedicated/VPS Servers, SSH, and Plesk say:

    [...] with Mod_Rewrite and Mod_Magnet based on asteriosk.gr method.  This method supposedly improves upon the Tempe.st method  below but I did not notice any [...]

  9. 28/09/2009chris say:

    quick q: why do you set the content-type to be blank?

  10. 29/09/2009Asterios Katsifodimos say:

    Hi Chris,

    The content-type=”" does not need to be blank as I see here:
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

    I suppose that the best would be to set like this:
    lighty.header["Content-Type"] = “text/html”

    Good point!

  11. 7/10/2009Joop say:

    YES! Thank you, this worked for me…

  12. 13/11/2009gomobi say:

    I think you can use Hyper Cache under Lighttpd without extra configuation

  13. 13/11/2009Asterios Katsifodimos say:

    Yes you can, but you don’t avoid the PHP subsystem calls. This way, you bypass the PHP subsystem completely! If you need a straight drop-in cache, you can go for Hyper Cache, it will not make much difference in a low-visit site.

  14. 8/01/2010tk say:

    Hi,

    it did not work out for me, perhaps this is a lighty 1.4 VS 1.5 issue.. anyhow, the above lua would never cache the index here is my solution: http://pro.to.co.ls/super-cache-lighttpd-lua (including some other performance tweaks)

  15. 17/01/2010wrongy say:

    Your lighttpd.conf code is wrong because the HTML code written into the real code.

Write a comment: