nginx server configuration for a wordpress instance served from a URL’s subdirectory

You want to serve a wordpress instance on a website’s domain url but not at the path’s root, you want it under a sub-directory, for example “blog”, the same as this blog: 

Here’s how my NGINX’s server block for ‘’ looks like at the moment (https/ssl hasn’t been configured yet)

server {
  listen 80;
  listen [::]:80;
  root /media/ebs/data/websites/;
  index index.php index.html index.htm;

  # wordpress lives at
  rewrite ^/blog/wp-admin/(.*) /blog/wp-admin/$1;
  #search redirect                                                                                                       
  rewrite ^/blog/(.*)s=(.*)$ /blog/index.php?s=$2;
  try_files $uri $uri/ /blog/index.php$is_args$args;

  location ~ \.php {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    include fastcgi_params;
  location ~ \.git {
    deny all;

Here is the equivalent in lighttpd, too bad lighttpd has no plans for HTTP2, it’s much friendlier and flexible to configure than nginx in my humble opinion.

$HTTP["host"] =~ "^$|^$" {

  $HTTP["url"] =~ "\.git" {
     url.access-deny = ("")

  url.rewrite = (
            "^/blog/wp-admin/(.*)" => "$0",
            "^/blog/(.*)\.(.+)$" => "$0",
            "^/blog/(.+)/?$" => "/blog/index.php/$1"

I used to host this website and wordpress on lighttpd, lighttpd’s config file is very powerful, it’s all based on matching server variables and applying rules, I will miss it dearly, things like having a compressed file cache and it’s flexibility, but I have to move on to nginx if I want to use http2, the lighttpd has no plans for http2 support and it’s just much faster and efficient than http 1.1.

[SYSADMIN] Serve your WordPress cached pages directly with lighttpd and not PHP

Optimizing Your WordPress Cache Loads in Lighttpd.

If you don’t configure your wordpress virtual host properly in lighttpd, your wordpress cache will still make use of PHP.

Wouldn’t it be nice if all those cached requests were served directy from the webserver as the static files that they are, bypassing the CPU/memory load PHP can have, and use those resources for otherthings?

Install and Enable mod_magnet

For this to occur with lighttpd, you will need mod_magnet, so assuming you’re on a Ubuntu/Debian based linux distro let’s make sure we have it installed.

sudo apt-get install lighttpd-mod-magnet

Then let’s make sure it’s enabled, you can do this manually on your lighttpd.conf by adding “mod_magnet” to the list of enabled modules…

server.modules = (

or you can do it the lighty way:

sudo lighty-enable-mod magnet

(this simply makes a symlink to the 10-magnet.conf file inside /etc/lighttpd/conf-enabled which lighty will check upon startup)

The cache logic script that will be executed by lighttpd

Now, on your wordpress directory, create a file called rewrite.lua and paste the following script in it:

function log(str)
   -- wanna tail -f a log to see what's happening    
   fp ="/path/to/some/lua.log","a+")
   fp:write(str .. "\n")

function serve_html(cached_page)
    if (lighty.stat(cached_page)) then
        lighty.env["physical.path"] = cached_page
        return true
        return false

function serve_gzip(cached_page)
    if (lighty.stat(cached_page .. ".gz")) then
        lighty.header["Content-Encoding"] = "gzip"
        lighty.header["Content-Type"] = ""
        lighty.env["physical.path"] = cached_page .. ".gz"
        return true
        return false

if (lighty.env["uri.scheme"] == "http") then
    ext = ".html"
    ext = "-https.html"

cached_page = lighty.env["physical.doc-root"] .. "/wp-content/cache/supercache/" .. lighty.request["Host"] .. lighty.env["request.orig-uri"]
cached_page = string.gsub(cached_page, "//", "/")
cached_page = string.gsub(cached_page, lighty.request["Host"] .. "/index.php", lighty.request["Host"])

attr = lighty.stat(cached_page)

if (attr) then
    query_condition = not (lighty.env["uri.query"] and string.find(lighty.env["uri.query"], ".*s=.*"))
    user_cookie = lighty.request["Cookie"] or "no_cookie_here"
    cookie_condition = not (string.find(user_cookie, ".*comment_author.*") or (string.find(user_cookie, ".*wordpress.*") and not string.find(user_cookie,"wordpress_test_cookie")))

    if (query_condition and cookie_condition) then
        accept_encoding = lighty.request["Accept-Encoding"] or "no_acceptance"

        if (string.find(accept_encoding, "gzip")) then
            if not serve_gzip(cached_page) then 
        --log('cache-hit: ' .. cached_page)
    --log('cache-miss: ' .. cached_page)

Configuring your vhost in lighttpd for WordPress redirects and direct cache serves without php.

Then on your vhost configuration in lighttpd.conf add the following towards the end.
(Fix paths if you have to)

var.wp_blog = 1

magnet.attract-physical-path-to = ( server.document-root + "/rewrite.lua" )

url.rewrite-if-not-file = (
   "^/(wp-.+).*/?" => "$0",
   "^/(sitemap.xml)" => "$0",
   "^/(xmlrpc.php)" => "$0",
   "^/(.+)/?$" => "/index.php/$1"

Restart your lighttpd sudo service lighttpd restart

Now watch how your PHP processes breathe a lot better and you page loads are insanely faster.

You’re welcome 🙂

What to do when lighttpd won’t start and won’t give out any error output?

So you upgraded your server, or just all of a sudden you try to start lighttpd, it says the server started ok, but you check and there’s no lighttpd process.

You then go after your error log files, and nothing… what the fuck is happening?

try this to attempt to debug.

sudo strace -ff /usr/local/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

In my case the output showed me lighttpd was having permission issues trying to access a log file…

[pid 28073] close(5) = 0
[pid 28073] munmap(0x7fe884c71000, 4096) = 0
[pid 28073] write(3, "2015-02-04 11:04:23: (log.c.164)"..., 49) = 49
[pid 28073] close(2) = 0
[pid 28073] open("/dev/null", O_RDWR) = 2
[pid 28073] brk(0x2203000) = 0x2203000
[pid 28073] open("/home/bh/access.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = -1 EACCES (Permission denied)
[pid 28073] write(3, "2015-02-04 11:04:23: (log.c.118)"..., 98) = 98
[pid 28073] write(3, "2015-02-04 11:04:23: (server.c.1"..., 83) = 83

lighttpd, allow “Access-Control-Allow-Origin:*” headers on the server status page

Maybe there’s someone out there who needs to read the output of lighttpd’s status for monitoring purpose like me tonight, and also, like me, you want to do this using JavaScript, but your browser gives you this nasty error:

XMLHttpRequest cannot load Origin is not allowed by Access-Control-Allow-Origin.

lighttpd allows you to add a custom header for all requests by adding this in a given context:

[perl]setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )[/perl]

For this to work, you must enable the mod_setenv.

But if you don’t enable this module, before you enable your mod_status module, you will never see the custom headers come out of your lighttpd HTTP response header output.

So make sure you enable mod_setenv like this:

server.modules = (
# "mod_simple_vhost",
"mod_setenv", #before mod_status, very important!
# "mod_evhost",


The header output of your lighttpd status page should look like this now:

Date:Wed, 30 Nov 2011 01:27:04 GMT

Hope this helps you.

How to filter logs in lighttpd

I usually don’t keep lighttpd access logs turned on to avoid writing for every read, but there are times when you need to monitor what’s going on, and you’d like to have a high signal-to-noise ratio so it might be convenient to ignore all requests to .gif, .png, .jpg, .css, .ico and other urls on your webserver.

To only log certain files, or to NOT log certain files, you resolve this the same way as you do every other thing in lighttpd… by matching lighttpd variables to regular expressions and applying the settings where they match.

# www -> the main website
$HTTP["host"] =~ "^$" {
server.document-root = "/var/www/"
server.errorlog = "/var/log/lighttpd/"

#Only log non-css and images
<strong>$HTTP["url"] !~ "(.css|.jpg|.gif|.png|.ico)$" {
accesslog.filename = "/var/log/lighttpd/"

So in this example, we only log, where the URL doesn’t end with any of the “.css”, “.jpg”, “.gif”, “.png” or “.ico” extensions. We filter those out.

Hope this works for you.