WordPress GET Variables with lighttpd

Support this website by purchasing prints of my photographs! Check them out here.

Due to RAM restrictions on various servers I've had to use, I learned to axe Apache a long time ago. I've replaced it with lighttpd, although I'll probably be transitioning over to nginx sooner or later (it's what we use at work, and seems to be even lighter in the memory consumption department). Therefor, all of the sites running PHP on my webserver sit behind lighttpd, which consists of several WordPress based sites.

For his website, which is to sell products with inventory which gets reduced, I chose to install Woocommerce. I've used other Woo WordPress products before, and it looks like one of the best WordPress eCommerce solutions. Unfortunately, Woocommerce doesn't work all that well with lighttpd, or more specifically, with lighttpd using the server.error-handler-404 configuration for handling URL routing. If you google lighttpd WordPress configuration, this is the most commonly recommended method for grabbing dynamic URLs.

The problem is that when lighttpd has the server.error-handler-404 in place for grabbing URLs, the GET parameters on the original request are NOT passed along to the index.php file. One could go to the root of the website and add a GET parameter and it would work fine, e.g. example.com/?a=b, but as soon as a page was requested which doesn't exist, the GET parameter would be lost, e.g. example.com/store?a=b.

The solution for this problem isn't complex by any means. If you inspect the $_SERVER variable on a request which was losing the GET parameters, you can see they're still available in $_SERVER[‘REQUEST_URI']. So all we have to do is grab the URI, after the first question mark, parse the variables, and replace the global GET parameter. The following code, when added to the top of the main index.php file, will solve this issue:

$question_pos = strpos($_SERVER['REQUEST_URI'], '?');
if ($question_pos !== false) {
        $question_pos++; // don't want the ?
        $query = substr($_SERVER['REQUEST_URI'], $question_pos);
        parse_str($query, $_GET);
}

Also, here's the lighttpd.conf settings that are recommended for using WordPress with lighttpd:

$HTTP["host"] =~ "(^|\.)example\.com$" {
        server.document-root = "/var/www/example.com"
        server.errorlog = "/var/log/lighttpd/example.com/error.log"
        accesslog.filename = "/var/log/lighttpd/example.com/access.log"
        server.error-handler-404 = "/index.php?error=404"
}

Tracking down the source of the problem for Woocommerce was pretty difficult. It wouldn't allow items to be removed, couldn't add items while viewing the item page, although it would allow an item to be added while viewing a listing of items. AKA it sometimes worked and sometimes didn't.

The root of the problem here is two-fold. First, lighttpd doesn't pass GET parameters along with the error handler directive. Second, Woocommerce should not be using GET parameters for persisting changes to the server. A GET request is intended to be used for just that, getting information from a server. A POST request is intended for sending changes to the server. While talking with Woo tech support, one of the things they kept asking me is if my host was caching requests. I said no, since it's a VPS I'm in control of the caching, and that domain has none. If Woocommerce were to switch over to using POST requests for persisting user cart changes, it would save their customers from having these caching issues (POST requests are never cached), and would have the side effect of allowing lighttpd to work without this code change.

There is a big shortcoming with this solution. When the administrator of the website updates WordPress, the changes in index.php could be overwritten. A better method to inject this code would be to write a WordPress plugin, and ensure that it is executed before the Woocommerce code is run. An even better solution would be to have more complex lighttpd rules with regular expressions to capture requests and route them all accordingly, without the need for the server.error-handler-404 code, but I don't know lighttpd configuration that well to come up with a solution.

Thomas Hunter II Avatar

Thomas has contributed to dozens of enterprise Node.js services and has worked for a company dedicated to securing Node.js. He has spoken at several conferences on Node.js and JavaScript and is an O'Reilly published author.