Raspberry Pi Lighttpd Optimisation For WordPress Owncloud and More

So after the popularity of my post regarding optimisation of apache in a pure LAMP setup on the Raspberry Pi I want to talk about optimising lighttpd for the Raspberry Pi also.

During this tutorial I will go through installing PHP APC, ensuring we have caching optimised for our static content, ensuring we have fast cgi enabled and setting up our compression with gzip correctly.

One of the main reasons I decided to switch away from lighttpd on my first attempt of using my Pi as a web server was that it seemed a bit more involved in setting everything up. On the other hand Apache seemed to have a lot of things enabled out of the box and there were a ton of resources out there to help along the way. However Apache has its downfalls, it is simply too resource hungry for the poor Raspberry Pi and after some botched up settings I was forced to return my Pi back to its default settings and kicked off my usage of Lighttpd once again. After using Apache for a few months it was really refreshing to see how a vanilla installation of Lighttpd was able to offer so much out of the box speed. The resource usage dropped, the number of processes dropped and all in all the amount of small nigling issues I had been facing when trying to optimise Apache on such a low end machine as the Pi has also gone away.

So.. for the rest of this tutorial I will assume that you have Lighttpd installed. It is very simply by the way you can simply do..

sudo apt-get install lighttpd

Now on to the real reason you are here, optimisation!

Fast CGI

I will not go into the details here around what fast cgi is, just think of it as awesome and lightweight and more awesome. I explained more about it in my last optimisation tutorial for apache so if you are really interested to learn more why not take a look!

So to kick things off you will be pleased to know this is quite easy to install with lighttpd and especially on the raspberry pi. Lets start by getting the php5-cgi package.

sudo apt-get install php5-cgi

Once the package and all of the dependencies have installed we need to edit our lighttpd configuration file and also our php.ini file. Do not worry to much about where I show my php.ini file location it can differ on everyones installation so it might take a few attempts to locate it correctly.

To edit my php.ini file I carry out the following.

sudo nano /etc/php5/cgi/php.ini

Once you are in the file hit Ctrl+w and search for “cgi.fix_pathinfo”.
Uncomment the line where it says:

;cgi.fix_pathinfo=1

to show

cgi.fix_pathinfo=1

Alternatively you can just type out the above line at the end of your file if you are less comfortable with doing a text search.

We can now dive in to lighttpd.conf.

sudo nano /etc/lighttpd/lighttpd.conf

From here we need to enable our server module “mod_fastcgi”. At this point you may want to jump to the end of this tutorial and copy and paste my standard module list. Comment out everything you do not need but remember to leave in “mod_fastcgi” as we now want this enabled.

The final edit is to let our lighttpd.conf file know we want to push php through fastcgi. Add the following lines to your configuration.

fastcgi.server = ( ".php" => ((
                     "bin-path" => "/usr/bin/php5-cgi",
                     "socket" => "/tmp/php.socket"
                 )))

From here the final step is to restart out lighttpd server.

sudo service lighttpd restart

There we go, fastcgi support for php.This will give us a nice performance increase due to a reduction in processor usage and resource usage… lovely.

PHP APC
We will now install PHP APC which is easily one of the best changes you can make to really give you server a speedy push in the right direction.

We first need to install the php extension installer PECL.

sudo apt-get install libpcre3-dev php-pear php5-dev build-essential

We now grab the apc extension with pecl.

sudo pecl install apc

This installation takes some time and there are questions to answer. For now just take the default answer to each by hitting your enter key apart from when Apache is mentioned, just select no for any questions related to Apache.

We now create our configuration for apc.

sudo nano /etc/php5/cgi/conf.d/apc.ini

Add the following lines to the file.

extension=apc.so
apc.enabled=1
apc.shm_size=30

Now restart lighttpd

sudo service lighttpd restart

BOOM! another awesome package installed and in improvement on our lighttpd speeds.

Expiration
Expiration is important. It essentially allows us to serve static files to a user and tell the users browser how long it should keep the file for. Why is this important? because it is useless to keep serving a user with the same file on the fly. That is extra requests, extra data transfer and extra work for our beloved Raspberry Pi that we do not need to keep doing.

By adding a date to which static content is valid for the browser will ask for the file then get the request cancelled because it realises oh yea I have this file already I do not need to ask for it. This pushes the processing of the file back on to the client where there will be a lot less rendering time.

So what do I mean by static content? Well I do not mean php, php is pretty dynamic for the most part. I’m talking more about those javascript files, stylesheets, images, html files , text files etc that rarely change. They just sit there making everything look pretty.

Lets get in to configuring lighttpd to set expiration values. You may wish to take note this is just my setup, it works fine for me. You may wish to tweak settings depending on how much your website changes but for me wordpress and my other programs are pretty damn static outside of the realms of php so I set expiration on as much as I can.

First we need to open up our lighttpd configuration file.

sudo nano /etc/lighttpd/lighttpd.conf

Next we need to take a look at our module list and enable mod_expire or add it in. It needs to be one of th every last modules to be enabled. Once again take a look at the end of this post to see the standard order I recommend you to use.

In my cut down version this is how my order looks.

server.modules = (
        "mod_redirect",
        "mod_alias",
        "mod_access",
        "mod_fastcgi",
        "mod_compress",
        "mod_expire"

)

Once again notice mod expire is one of the very first modules to be loaded (but shows as last in the list).

Now we need to work on the rest of our configuration. You will need to add in some code as shown below.

$HTTP["url"] =~ ".(jpg|gif|png|css|js|svg)$" {
    expire.url = ( "" => "access 7 days" )
}
etag.use-inode = "enable"
etag.use-mtime = "enable"
etag.use-size = "enable"
static-file.etags = "enable"

Firstly I declare that anything in my url that contains jpg,css,js etc etc will be cached. The caching/expiry will last from the day they access the file up until 7 days after. This is great when you think visitors will visit multiple pages and may come back within the next week to read more articles.

My list of files I have added expiration to are typically scripts and images. I could have added in html files but I can not guarantee that they will always remain static so I thought it best to leave these out.

You will also notice I have declared etags. These are important for our images. Without an etag some browsers end up doing a bit of extra work so we add in these tags to ensure the browser can validate the file from its cache easily.

Compression

Now I want to talk about compression. Browsers are able to unpack gzip files and read what is within and serve them to the user. Compression can have a huge effect on the speed of your site. I have seen my requests go from 27 down to 3 purely because I have been able to not just gzip the file down to be a few kb’s instead of 100′s of kb’s but you can also combine files in to one gzip. I wont go too much in to how this is done but once again just remember this is awesome for our user experience. Compression is used across nearly every site out there to improve performance and it is definitely something we need to enable to ensure our Raspberry Pi is not bogged down by a number of large files having to be transmitted to the user.

To enable gzip compression we need to carry out the following.

Firstly lets jump back in to our lighttpd.conf file.

sudo nano /etc/lighttpd/lighttpd.conf

From here you want to uncomment or add the line “mod_compress”. Below is my setup, once again pay attention to the order of the modules!

server.modules = (
        "mod_redirect",
        "mod_alias",
        "mod_access",
        "mod_fastcgi",
        "mod_compress",
        "mod_expire"

)

You then need to add in some configuration settings. I will show you mine below and then go in to more detail about what they mean.

# compression settings for gzip
compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ("text/xml","application/x-javascript", , "application/javascript", "text/javascript", "text/x-js", "text/css", "text/html", "text/plain", "image/png", "image/gif", "image/jpg", "image/svg+xml", "application/xml")

You may notice that I declare A LOT of difference filetypes I want to compress. You will see a lot of other tutorials out there stick with just css/javascript and html but Why? there are other resources out there that would benefit massively from compression. These include and by no means are limited to images,xml,plain text, cross domain scripts and much more. Anything you serve can be compressed and can be cached so lets do it! The only things I decided not to compress were my dynamic php files these can be compressed within php itself which I will explain in a moment.

Once you have added the above settings in to your lighttpd.conf we will now need to ensure a compression directory is available as per the compress.cache-dir declaration.

Carry out the following commands to ensure everything is available.

sudo mkdir /var/cache/lighttpd/compress/
sudo chown www-data:www-data /var/cache/lighttpd/compress/

We can now move on to PHP compression. Open up your php.ini file as per below.

 sudo nano /etc/php5/cgi/php.ini

You want to look for the line zlib.output_compression and set it to On as shown below.

zlib.output_compression = On 

Finally we can restart lighttpd and all is set!

sudo service lighttpd restart

Important Reference Notes

It is very important to realise with Lighttpd the declaration order of your modules has to be in the correct order. So please find below a list which is set out in the order you need. Whilst you will not need to enable every module I will say that it is probably best to copy and paste the code in to your lighttpd config file and then just comment out anything you do not need to enable.This way if you need to add in further modules to your config in the future you simply need to uncomment them and not have to keep looking up the correct order.

server.modules = (
"mod_rewrite",
"mod_redirect",
"mod_alias",
"mod_access",
"mod_auth",
"mod_status",
"mod_simple_vhost",
"mod_evhost",
"mod_userdir",
"mod_secdownload",
"mod_fastcgi",
"mod_proxy",
"mod_cgi",
"mod_ssi",
"mod_compress",
"mod_usertrack",
"mod_expire",
"mod_rrdtool",
"mod_accesslog" )

The execution of the above items is in reverse order. So starting from the bottom module you lighttpd will start enabling and executing. This is why there is such an important on of the order of the list.

You may also like...

  • Bsense

    Dear Master,

    Fist of all, let me express my gratitude for that comprehensive tutorial in how-to fine tune this little wonder.

    I have followed your tutorial, I may say that for newbies like me, a first prevrious:
    sudo apt-get update, is necessary to ensure that lighttpd release is correctly downloaded (otherwise fails).

    BUT I am not able to succeed in the PECL installation
    sudo apt-get install pecl php5-dev build-essential
    It returns an error: E: unable to locate package pecl

    I dont know how to proceed, may you give some light to these wandering soul?

    • MrMobberley

      I have added in php5-pear instead of pecl. Let me know if it works now.

      • Bsense

        Dear Mr Mobberley,

        I guess that php5-pear should be php-pear instead. In my humble opinion libpcre-dev should also be installed
        So the final command would be:
        sudo apt-get install libpcre3-dev php-pear php5-dev build-essential

        The pecl install apc, it would require the super user, so:
        sudo pecl install apc

        By adding these minor changes it seems to work in my rPI! (Humbly I hope it helps to improve this excellent tutorial)

        BTW, in this tutorial I wasn’t able to see the installation of the fastcgi (the module is activated in the lighttpd.conf, but there is no apt-get for it) while in the apache optimization it was clearly detailed. We the people that did not install it for apache or whatever, how should we install it?

        Thanx in advance

        • MrMobberley

          Thanks I will add the additional modules. It is hard to capture all dependencies as some people may have them installed already from previous orojects. FastCGI is supported out of the box with lighttpd mod_fastcgi as soon as you install php5 – cgi. This is not the case with apache.

          • Bsense

            Thank you sir!

            One final question, according to your large expertise, which is the best SQL database server to install “on top” of this configuration:
            sqlite, sqlite3, mysql?

            My goals are to define a low traffic site (Machine to Machine ERP + PLC’s) for industrial purposes. I have a former system based in a PC with a WAMP, with some pages in PHP that connect to the local MySQL webserver. The most straight forward solution would be to install MySQL in the rPI, allowing me to reuse all of my PHP pages (not so many at all), but I am afraid of performance penalities if compared to sqlite. Specially taking into account the fine tuning achieved following your recommendations

            Any idea on that?

            Thank you all.

          • MrMobberley

            To be honest I would go with mysql purely because it will allow you to re-use your existing developed code. Also mysql has many optimisations you can use if you take a look on google. I use mysql for all my database needs on the rpi and it has not done me any harm. It is more resource hungry than sqlite etc but it is not a complete killer like some people make out. I think the biggest optimisation is within the webserver e.g. lighttpd/nginx etc and not the database side of things.

  • david

    the compression didn’t work for me
    I commented the file types out but still got something like this:
    source: /etc/lighttpd/lighttpd.conf line: 48 pos: 1 parser failed somehow near here: (EOL)

    • MrMobberley

      Hi if you can post your configuration I will take a look. The error likely looks to be a syntax issue such as a missing comma, bracket or brace etc

      • David

        hi
        here is my .conf:

        server.modules = (
        “mod_access”,
        “mod_alias”,
        “mod_compress”,
        “mod_redirect”,
        #”mod_rewrite”,
        “mod_fastcgi”,
        “mod_expire”
        )

        server.document-root = “/var/www”
        server.upload-dirs = ( “/var/cache/lighttpd/uploads” )
        server.errorlog = “/var/log/lighttpd/error.log”
        server.pid-file = “/var/run/lighttpd.pid”
        server.username = “www-data”
        server.groupname = “www-data”
        server.port = 80

        index-file.names = ( “index.php”, “index.html”, “index.lighttpd.html” )
        url.access-deny = ( “~”, “.inc” )
        static-file.exclude-extensions = ( “.php”, “.pl”, “.fcgi” )

        compress.cache-dir = “/var/cache/lighttpd/compress/”
        compress.filetype = ( “application/javascript”, “text/css”, “text/html”, “text/plain” )

        # default listening port for IPv6 falls back to the IPv4 port
        include_shell “/usr/share/lighttpd/use-ipv6.pl ” + server.port
        include_shell “/usr/share/lighttpd/create-mime.assign.pl”
        include_shell “/usr/share/lighttpd/include-conf-enabled.pl”

        fastcgi.server = ( “.php” => ((
        “bin-path” => “/usr/bin/php5-cgi”,
        “socket” => “/tmp/php.socket”
        )))

        $HTTP["url"] =~ “.(jpg|gif|png|css|js|svg)$” {
        expire.url = ( “” => “access 7 days” )
        }
        etag.use-inode = “enable”
        etag.use-mtime = “enable”
        etag.use-size = “enable”
        static-file.etags = “enable”

        # compression settings for gzip
        compress.cache-dir = “/var/cache/lighttpd/compress/”
        compress.filetype = (“text/xml”,”application/x-javascript”, , “application/javascript”, “text/javascript”, “text/x-js”, “text/css”, “text/html”, “text/plain”, “image/png”, “image/gif”, “image/jpg”, “image/svg+xml”, “application/xml”)

        • MrMobberley

          I have also just noticed on this line:

          (“text/xml”,”application/x-javascript”, , “application/javascript”, 
          

          There seems to be a couple of errors, firstly you have a , , with nothing between as boldened. Secondly the ” quotes you are using dont seem to be correctly compatible. It might just be how wordpress is showing them in your comment but they seem to be slanted which suggests they are holding some formatting that linux will not like. Try adding in the quotes manually yourself so “text/xml” instead of a copy and paste. Also finally if you are splitting your compress.filetype over multiple lines as per the commented code I would put everything in to one line. I know this makes it annoying to read when using the text editor but it will ensure there are no whitepsaces or anything like that messing up your configuration.

      • David

        oh and btw its the last line what does the error
        I tried changing to only one filetype like:
        compress.filetype = “text/xml”
        thanks a lot

        • MrMobberley

          Hi David,

          Thank you for the info. To make this simpler here is an exact copy of my lighttpd configuration for my raspberry pi which I know does not error:

          server.modules = (
          	"mod_redirect",
          	"mod_alias",
          	"mod_access",
          	"mod_fastcgi",
          	"mod_compress",
          	"mod_expire"
          	
          ) 
          server.document-root = "/var/www"
          server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
          server.errorlog = "/var/log/lighttpd/error.log"
          server.pid-file = "/var/run/lighttpd.pid"
          server.username = "www-data"
          server.groupname = "www-data"
          server.port = 81
          index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
          url.access-deny = ( "~", ".inc" )
          # compression settings for gzip
          compress.cache-dir = "/var/cache/lighttpd/compress/"
          compress.filetype  = ("text/xml", "application/x-javascript", "application/javascript", "text/javascript", "text/x-js", "text/css", "text/html", "text/plain", "image/png", "image/gif", "image/jpg", "image/svg+xml", "application/xml")
          
          
          
          # default listening port for IPv6 falls back to the IPv4 port
          include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
          include_shell "/usr/share/lighttpd/create-mime.assign.pl"
          include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
          fastcgi.server = ( ".php" => ((
                               "bin-path" => "/usr/bin/php5-cgi",
                               "socket" => "/tmp/php.socket"
                           )) )
          
          #uncomment below for aspx
          $HTTP["url"] =~ "^/mono/"{ fastcgi.server = (
          		"" => ((
          #To be added
          "socket" => "/tmp/fastcgi-mono-server4",
          "bin-path" => "/usr/bin/fastcgi-mono-server4",
          "bin-environment" => (
          "PATH" => "/bin:/usr/bin",
          "LD_LIBRARY_PATH" => "/usr/lib:",
          "MONO_SHARED_DIR" => "/tmp/",
          "MONO_FCGI_LOGLEVELS" => "Error",
          "MONO_FCGI_LOGFILE" => "/tmp/fastcgi.log",
          "MONO_FCGI_ROOT" => server.document-root, 
          "MONO_FCGI_APPLICATIONS" => "/mono/:/var/www/mono/" ),
          "max-procs" =>4,
          "check-local" => "disable"
          			)) )
          }
          
          $HTTP["url"] =~ "^/Project/"{ fastcgi.server = (
          		"" => ((
          #To be added
          "socket" => "/tmp/fastcgi-mono-server4-Project",
          "bin-path" => "/usr/bin/fastcgi-mono-server4",
          "bin-environment" => (
          "PATH" => "/bin:/usr/bin",
          "LD_LIBRARY_PATH" => "/usr/lib:",
          "MONO_SHARED_DIR" => "/tmp/",
          "MONO_FCGI_LOGLEVELS" => "Error",
          "MONO_FCGI_LOGFILE" => "/tmp/fastcgi.log",
          "MONO_FCGI_ROOT" => server.document-root, 
          "MONO_FCGI_APPLICATIONS" => "/Project/:/var/www/Project/" ),
          "max-procs" =>4,
          "check-local" => "disable"
          			)) )
          }
          $HTTP["url"] =~ "^/test/"{ fastcgi.server = (
                          "" => ((
          #To be added
          "socket" => "/tmp/fastcgi-mono-server4-test",
          "bin-path" => "/usr/bin/fastcgi-mono-server4",
          "bin-environment" => (
          "PATH" => "/bin:/usr/bin",
          "LD_LIBRARY_PATH" => "/usr/lib:",
          "MONO_SHARED_DIR" => "/tmp/",
          "MONO_FCGI_LOGLEVELS" => "Error",
          "MONO_FCGI_LOGFILE" => "/tmp/fastcgi.log",
          "MONO_FCGI_ROOT" => server.document-root,
          "MONO_FCGI_APPLICATIONS" => "/test/:/var/www/test/" ),
          "max-procs" =>4,
          "check-local" => "disable"
                                  )) )
          }
          
          
          
          # website re-write aliases
          alias.url += ( "/pma/" => "/usr/share/phpmyadmin/" )
          # expire information
          $HTTP["url"] =~ ".(jpg|gif|png|css|js|svg)$" {
              expire.url = ( "" => "access 7 days" )
          }
          etag.use-inode = "enable"
          etag.use-mtime = "enable"
          etag.use-size = "enable"
          static-file.etags = "enable"
          
          #protect data directories
          $HTTP["url"] =~ "^/data/" { url.access-deny = ( "" ) }
          #asp.net stuff
          # Add index.aspx and default.aspx to the list of files to check when a directory is requested.
          index-file.names += ( "index.aspx", "default.aspx", "index.cshtml", "default.cshtml" )
          fastcgi.map-extensions = (
                  ".asmx" => ".aspx",
                  ".ashx" => ".aspx",
                  ".asax" => ".aspx",
                  ".ascx" => ".aspx",
                  ".soap" => ".aspx",
                  ".rem" => ".aspx",
                  ".axd" => ".aspx",
                  ".cs" => ".aspx",
                  ".config" => ".aspx",
                  ".dll" => ".aspx" )
          
          #uncomment below for aspx
          

          I know all of it will not be relevant to your setup as I am also running MVC/Asp.net on this configuration but hopefully it willa llow you to compare the relevant parts.

          • David

            hi
            thx a lot. I found my mistake now…. I already had a compression section in that file :-/ lol sorry. thx for your time

          • MrMobberley

            Ah! Lol I missed that.. good catch. Hope it improves your speeds now anyway. Good luck

  • Pingback: Ditching Google Services for ownCloud on the Pi | rhodey - An Honest Effort

  • http://vdna.be Floris

    I’m also running a debian based lighttpd wordpress setup and was wondering if excess writing will wear out my SD-card causing me to lose all my content (backing up the mysql DB should be done anyway however). Do you have any experience with the wear of the SD-card on which the RPI is writing its log files and temporary files? Mounting /tmp and /var/log as a tmpfs (i.e. a ram-disk) would probably help alot.
    Congrats on the blog by the way, mine is up at http://vdna.be (still empty at the moment though).

  • kelxon

    Your portion:
    fastcgi.server = ( “.php” => ((
    “bin-path” => “/usr/bin/php5-cgi”,
    “socket” => “/tmp/php.socket”
    )) )

    needs to be:
    fastcgi.server += ( “.php” =>
    ((
    “bin-path” => “/usr/bin/php5-cgi”,
    “socket” => “/tmp/php.socket”
    ))
    )

    I couldn’t get lighttpd to restart without the “+” sign; kept on getting error at that line when trying to restart lighttpd.

  • Pingback: Volume 2: lighttpd: An Easy Fling | Not a Holocron

  • Pingback: Raspberry Pi Mono MVC Razor View Engine ASP.net XSP / FastCgi / Lighttpd Implementation | Raspberry Pi | 512MB of Awesome

  • Pingback: Speeding things up

  • gb

    Hello,

    nice tutorial, but I have two remarks and one question.

    1. in x86 Debian php-apc is installable with apt-get. Is there no php-apc package in RaspBians repos (I have no raspi available in the moment) ?

    2. compression of jpg, png and (maybe) gif is kind of a nonsense in my humble opinion, as (at least) jpg and png are already compressed with highly effective compression methods, and a double compression is mostly a waste of cpu cycles (which are rare on such a small platform like the raspi). gif (which is patent encumbered and is LZW compressed) should be rare and mostly substituted through png. png is deflate compressed, which is realized with zlib, which is also the base for the deflate of Lighty.

    The question: what about Lighties xsendfile and owncloud ?

    I like your ideas regarding expiration.

    cheers, gb

  • Pingback: Installing Seafile Cloud File Server On Your Raspberry Pi | Raspberry Pi | 512MB of Awesome

  • Pingback: Raspberry Pi Lighttpd Optimisation For Wordpres...

  • Tommy Bergström

    This helped me a lot! Thanx!

  • Pingback: Farewell Raspbian… Hello PiBang! : The Unwritten Words

%d bloggers like this: