Server Fault is a question and answer site for system and network administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm trying to setup Wordpress multisite (subfolder structure) with nginx, but having a problem with this rewrite rule.

Below is the Apache's .htaccess, which I have to translate into nginx configuration.

RewriteEngine On
RewriteBase /blogs/
RewriteRule ^index\.php$ - [L]

# uploaded files
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule  ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule  ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]

Below is what I came up with:

server {
    listen 80;
    server_name_in_redirect off;
    expires 1d;

    access_log /srv/www/;
    error_log /srv/www/;

    root /srv/www/;
    index index.html;

    try_files $uri $uri/ /index.html;

    # rewriting uploaded files
    rewrite ^/blogs/(.+/)?files/(.+) /blogs/wp-includes/ms-files.php?file=$2 last;

    # add a trailing slash to /wp-admin
    rewrite ^/blogs/(.+/)?wp-admin$ /blogs/$1wp-admin/ permanent;

    if (!-e $request_filename) {
        rewrite ^/blogs/(.+/)?(wp-(content|admin|includes).*) /blogs/$2 last;
        rewrite ^/blogs/(.+/)?(.*\.php)$ /blogs/$2 last;

    location /blogs/ {
        index index.php;
        #try_files $uri $uri/ /blogs/index.php?q=$uri&$args;

    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /srv/www/$fastcgi_script_name;

    # static assets
    location ~* ^.+\.(manifest)$ {
        access_log /srv/www/; 

    location ~* ^.+\.(ico|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        # only set expires max IFF the file is a static file and exists
        if (-f $request_filename) {
            expires max;
            access_log /srv/www/; 

In the above code, I believe rewrite ^/blogs/(.+/)?(.*\.php)$ /blogs/$2 last; has no effect because when I look at the access_log file, I see the following line:

2010/09/15 01:14:55 [error] 10166#0: *8 "/srv/www/" is not found (2: No such file or directory), request: "GET /blogs/test/ HTTP/1.1"

(Here, 'test' is the second blog created using multisite feature) What I'm expecting is that /blogs/test/index.php gets rewritten to /blogs/index.php, but it doesn't seem to do that...

Am I overlooking something obvious?


share|improve this question

For one thing, I don't think you're going to get away with changing [_0-9a-zA-Z-]+ to .+ in your rewrite rules, but I don't think that's what's biting you here.

The rule rewrite ^/blogs/(.+/)?(.*\.php)$ /blogs/$2 last; isn't matching /blogs/test/index.php because the request is actually for /blogs/test/. The index.php isn't tacked on until four lines later, when Nginx looks for the index file for that directory. If I'm right, /blogs/test/ won't get rewritten, but /blogs/test/index.php (as put in the browser location bar) will.

That said, I think even then you might find that the (.+/)? part of the request matches nothing, and the (.*\.php) part matches the entirety of test/index.php resulting in essentially no rewriting at all, because of your imprecise wildcards as I noted in the first paragraph.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.