Clean URLs (Permalinks) for WordPress on Lighttpd

I’ve moved my blog in the last few days to a new bigger dedicated server (as well as some other sites I own). After doing some benchmarks (I plan to post those soon) I’ve decided to switch to Lighttpd. While the exact migration notes are the topic of another post, I can say that I’m fairly satisfied with the move.

After setting up the server, I started moving the blog. Importing the files and the database was pretty straight forward. But when I thought every thing is ready and I transfered the domain to the new server I’ve found out that none of my inner pages are accessible. The reason, as it turned up pretty quickly, is that the WordPress depends on Apache’s mod_rewrite to create the clean URLs (the so called permalinks). This actually posed two problems:

  1. WordPress depends on Apache’s mod_rewrite.
  2. WordPress used .htaccess files for the clean URLs configuration

Luckily, Lighttpd has its own mod_rewrite, however it operates a bit differently. The WordPress generated .htaccess has the following lines:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

This rules tells Apache to check if the requested URL is a file or directory that exist on the server, otherwise append the requested URL to index.php. The last statement can be written in Lighttpd’s mod_rewrite syntax as

"^/(.+)/?$" => "/index.php/$1"

If we only use this rule, the posts will load up fine, but all static files will not be available, that includes CSS files and images which isn’t desired. Apache handles this by checking if the requested URL exist as a file or directory. Unfortunately Lighttpd’s mod_rewrite has no way to check if a file exists. I’ve found out that a possible workaround for this is to use mod_magent which allows to embed LUA code in Lighttpd, and use it to check if files exist and handle the clean URLs. This seemed to me an overkill, and as some people noted it has some performance hit which I wanted to avoid.

I’ve figured out that I shouldn’t just translate the rewrite rules, instead I need to adapt them. In Apache we check if a file exist in order to be able to serve the static files. The directories are checked so the server would be able to list them. The problem is now to achieve those objectives in Lighttpd. First we don’t really need to allow the directories to be listed (actually I’ve planed to block this option in my blog a long time ago). So the rule about the directories can be fully ignored. All the static files that need to be served have an extension, this includes all the files I’ve uploaded. We note that WordPress generated permalinks never have a dot (‘.’) in their URLs. So we can exploit this behavior to achieve our goal. The following condition uses regular expressions to check if a given url has an extension, and if so serves it.

"^/(.*)\.(.+)$" => "$0"

The only difference from the Apache configuration is that the web-server itself returns the 404 message when a requested file is missing, instead of passing it to WordPress and letting it return the error.

To sum everything up, our mod_rewrite configuration should look like this:

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

As I noted before (the second problem), there is no equivalent to .htaccess files in Lighttpd. So this configuration has to go directly into the main configuration file in /etc/lighttpd/lighttpd.conf (or where it exists in your server). Make sure you’ve enabled mod_rewrite, as this is required. You can enable it by putting the following line some where above the block.

server.modules += ("mod_rewrite")

If you have multiple sites hosted, you should use include the url.rewrite block inside a conditional statement. For example my configuration looks like this

$HTTP["host"] =~ "(www.)?" {
	url.rewrite = (
		"^/(.*)\.(.+)$" => "$0",
		"^/(.+)/?$" => "/index.php/$1"

	[...other unrelated configurations...]

And I’ve enabled the mod_rewrite at the top of the /etc/lighttpd/lighttpd.conf file.

This workaround works well, as you can read this post now. If you run into problems, or have any question about why or how I did soemthing, don’t hesitate to comment.

62 thoughts on “Clean URLs (Permalinks) for WordPress on Lighttpd”

  1. Setup a webserver on an old android phone this morning. Put up a WP site for the heck of it. This tutorial is amazing, thanks!

  2. Hi, thank you for your guide.

    My WordPress + Lighttpd setup stopped working with the server.error-handler-404 = “/index.php” solution and gives me a 404 error unless I’m logged in to WordPress.
    So I tried to use your settings to solve the issue but it didn’t help. Any advice would be really appreciated.

  3. OK, this worked and solved the problem with permalinks, but when I have this code in my lighttpd.conf, I cannot get to my wordpress admin area to show – the URL in the browser shows “, but the page in the browser is simply the home page. Any thoughts on what might be causing this? If I remove you rewrite code, I regain access to the admin area, but then other stuff is broken.

  4. OK, I actually got it working. I got this line from another site and added it within the rule that you posted:

    Exclude some directories from rewriting

    "^/(wp-admin|wp-includes|wp-content|gallery2)/(.*)" => "$0",

    Now everything is working, as least as near as I can tell.

  5. thank you Eldon. I had the same problems like you , wordpress wp-admin link not working and another software in the same server not working.
    this article is incomplete. lucky with the comments.

  6. For anyone on Lighttpd with Gutenberg crashing on error TypeError: "e.visibility is undefined" (in the browser console).

    The line causing the Gutenberg crash in my case was:
    "^/(wp-.+).*/?" => "$0"

    I changed the above to "^/wp-admin/$" => "$0" and Gutenberg no longer crashes. So my virtual host config now looks like:

    $HTTP["host"] =~ "your-virtual-host-name" {
        server.document-root = "your/doc/root/here"
        url.rewrite-if-not-file = (
            "^/wp-admin/$" => "$0",
            "^/.*?(\?.*)?$" => "/index.php$1"
        server.error-handler-404 = "/index.php"

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.