Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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 example.com;
    server_name_in_redirect off;
    expires 1d;

    access_log /srv/www/example.com/logs/access.log;
    error_log /srv/www/example.com/logs/error.log;

    root /srv/www/example.com/public;
    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/example.com/public$fastcgi_script_name;

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

    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/example.com/logs/static.log; 

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/example.com/public/blogs/test/index.php" 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

1 Answer 1

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.