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]
</IfModule>

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.)?guyrutenberg.com" {
	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. XCache is great software. In my benchmark I compared between Apache with eAccelerator and Lighttpd with XCache and the results where very clear in favor of Lighttpd + XCache

  2. WordPress 404 messages works. As I said in the post, you will have to use mod_magnet if you want a different behavior for directories (an some people report performance hit with it). My suggestion is if you only need a different handling for a specific directory (or just a few) is to add an exception for it explicitly in the rewrite rules. BTW exactly what kind of behavior you want for directories? listing?

  3. How can I make directory exceptions so that the rewrite rules don’t affect other web interfaces that I am running? When I have the rule setup I can’t access anything else beyond wordpress.

  4. Hi Robert,
    If you want to add any exceptions you will have to add them before the wordpress code. For example if you want that no url-rewriting will be done for everything in directory called “temp” you will use
    url.rewrite = (
    “^/temp/” => “$0”,
    “^/(.*)\.(.+)$” => “$0”,
    “^/(.+)/?$” => “/index.php/$1”
    )
    Notice that it requires you to add a slash when you access the directory. By default it shows the directory listing when accessing it. If you want to add rules to override the ones used for WordPress just add them before.

  5. Hi Sudhaker,

    I’m afraid your solution is not the best known solution, as it has a major drawback – its use of mod_magent.

    The solution you describes uses the Lua scripting language to check if files exist. As I wrote in my post, several people reported performance hit when mod_magnet is used, and I personally believe it’s an overkill.

    Trying to directly translate work done on one platform (in this case Apache) to another one (Lighttpd) isn’t always the best, usually one should adapt to the strengths and weaknesses of each one. Ignoring it is like writing someone writing python code as he used to write C (procedural oriented), ignoring Python advantages and drawbacks.

    Sorry, but I believe you should be a bit humbler before you state you’ve the best solution.

    Regards,

  6. Sorry I had no intention to offend you. I called it ‘the best’ only because ‘it always works’ regardless what is your permalink pattern. For example, my favorite pattern for common sites is /%category%/%postname%.html which will fail with the suggested solution.

    I think any solution without mod_mangnet will be specific to pattern and any change in it may break permalink.

    I totally agree with your point about overkill because there will be a LUA script execution and file IO call for each and every request.

    I’m very interested in quantifying the performance penalty. I’ll try to run some stress test and publish my findings.

    Cheers,
    Sudhaker

  7. Hi Sudhaker,

    You didn’t offend me :).
    As I said your solution is very good, except the requirements of using Lua. Mabe doing some performance tuning (by using benchmarks), and finding a way to use some kind of cache for the IO operation (so that it won’t check if the file exists every time) can make it perform better.

    Regards,

  8. What do you do if you have a wp installation inside a folder under the domain root folder. I follow you indications for the domain tuxubuntu.com and all was great. But I need to do a other installation under tuxubuntu.com.

    tuxubuntu.com -> wp-installation
    tuxubuntu.com/folder -> wp-installation

    So when I write tuxubuntu.com/folder/ appears 404 error. if I write tuxubuntu.com/folder/index.php all is right. All the others permalinks are right under /folder/

    Tanks.

  9. Sorry but I comment a mistake, All the others permalinks under tuxubuntu.com/folder/ do not work too.

    Please help me. Tanks

  10. Hi Mario,

    Have you tried something like this?
    url.rewrite = (
    “^/folder/(.*)\.(.+)$” => “$0”,
    “^/folder/(.+)/?$” => “/folder/index.php/$1”,
    “^/(.*)\.(.+)$” => “$0”,
    “^/(.+)/?$” => “/index.php/$1”
    )

    The order of the statements is important (does the rewrite and stops after the first match). I believe It can also be done the following way
    url.rewrite = (
    “^/(.*)\.(.+)$” => “$0”,
    “^/folder/(.+)/?$” => “/folder/index.php/$1”,
    “^/(.+)/?$” => “/index.php/$1”
    )

  11. I Guy

    I used the first and second option (keep the second) where folder=windows, and almost all was right, except that in the specifically case of

    http://www.tuxubuntu.com/windows/

    don’t load the home start page. I need to put

    http://www.tuxubuntu.com./windows/index.php

    So the problem is when you click on the name of the blog because it redirects to

    http://www.tuxubuntu.com/windows/

    and appears a 404 error.

    Dou you have and idea about solve this? Tanks. Yo can check the problem visiting the webadress.

  12. Hi Mario,

    Try this
    url.rewrite = (
    “^/(.*)\.(.+)$” => “$0″,
    “^/windows/(.*)/?$” => “/windows/index.php/$1″,
    “^/(.*)/?$” => “/index.php/$1″
    )

    I’ve substituted the ‘+’ in the regular expression with ‘*’ so it will work correctly

  13. man, I wish I knew your site earlier. It works well and support other tags 🙂
    I wish you write more tips about lighttpd.
    I’m currently looking for the right setting for chrooting it.

    cheers

  14. Hi there,

    I was looking for something like this. And your solution seems to be the perfect one.

    But i have a small issue with this method. I am unable to access /wp-admin/ with this method. Therefore i’d to resort to the method explained earlier: “^/temp/” => “$0”,

    Unfortunately for me, this breaks my admin section. The CSS and/or JS files are not being loaded properly. But the front end works flawlessly.

    I’m using WP 2.71.

    Please let me know if you have a solution to this. Thanks.

  15. Hi Gayan,

    Try accessing /wp-admin/index.php, this what I do. It should work, and as far as I know, there it doesn’t cause any further problems in the admin (all the links there include the script name after the wp-admin/).

  16. Hi Dave,

    I’m familiar with the method suggestted in r00tshell. While changing the 404 error handler works, I find it the wrong tool for the job, things like mod_rewrite exist so we don’t have to make weird 404 handling hacks.

    You can drive a nail using a big wrench, but I think that a hammer is still a more appropriate tool for the job.

  17. Pingback: More Pie Media
  18. Guy,
    Statements

    RewriteRule . /index.php [L]

    and

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

    are actually not equvalent. RewriteRule does not append requested URL to index.php, but simply forwards the request to index.php. So the actual equivalent would be

    “^/(.*)/?$” => “/index.php”

    My guess is that WordPress examines $_SERVER[“REQUEST_URI”] to get to the original URL.

  19. Thanks for this, but it didn’t work for me as-is. Instead I had to add a ‘?’ to the end of the index.php..

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

    This is just for anyone who googles this page and has the same problem 🙂

  20. Super Thanks, works great! the only difference was i had to Uncomment a line in my lighttpd.config instead of appending it

    server.modules = (“mod_redirect”)

  21. +1 on the comment above.

    thanks for posting this. your tip worked out of the box for me to get my WP blog [previously hosted on apache] running with clean URLs on lighttpd.

    now if only someone could make it similarly easy to do likewise with drupal!

  22. Hello,

    How to make permalinks like this :
    /category/postname.html ??

    basically, how to make category folder works with this methods.

    thanks.

  23. Hello,

    I have a problem with my rewrite rule, after the rewrite some of the images are not displayed, I tried applying your code to my configuration but then I received a 404 error. Here is my config, kindly help me on this I have been trying and trying and still no luck with my problem.

    url.rewrite-once = (
    “^/folder\.one/$” => “/folder.ex1/index.php?sectionId=1&sectionName=Travel+%26+Fast”,
    “^/folder\.one/viewtopic\.php\?postid\=([a-zA-Z0-9]+)&channelName\=([a-zA-Z0-9]+)&sectionId\=([a-zA-Z0-9]+)” => “/folder.ex1/viewtopic.php?postid=$1&channelName=$2&sectionId=$3”
    )

    The first line of my code is to rewrite to this: “www.example.com/folder.one/” it was OK but unable to view the images of the page.
    then 2nd line is so that if i will click some of the links inside this rewrite it will be viewable, but unfortunately some of the images are not viewable.

    Help needed badly. Thanks all help will be greatly appreciated.

  24. For the people who have trouble access /wp-admin after implementing Guy’s method, try this:

    url.rewrite = ( “^/(wp-admin|wp-includes|wp-content)/(.*)” => “$0″,”^/(.*)\.(.+)$” => “/$0″,”^/(.+)/?$” => “/index.php?/$1” )

    After this the link for wp-admin works like a charm.

    It works for me at least.

  25. Hi Jesin, I’m not familiar with wp-supercache, but wp-cache worked out-of-the-box for me.

  26. hi,guy!
    i used your rewrite rule to config my wp on lighttpd. but all the urls have a “index.php” part. for instance, the url is “http://xleo.com/index.php/year/month/123”, and i want to change it to “http://xleo.com/year/month/123”.
    what should i do? please help me.
    🙂

  27. In the “Permalink Settings” select “custom structure” and set it to /%year%/%monthnum%/%day%/%postname%/

  28. I had tried this method before.
    The url can be shown in this format, but when I clicked the url, it was 404 page.
    🙁

  29. This sounds weird, are you sure you’ve configured your lighttpd correctly and reloaded the settings?

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.