Wordpress Multi-Site Converting Apache to Nginx

I’m trying to move a working Apache2 config to Nginx and failing
miserably.
The site has a main application, a custom CMS and a wordpress multi-site
with a few blogs. The CMS rules are setup and working, but I can’t get
the
wordpress rules to 1. Work and 2. Get executed ebcause the CMS rules are
being too greedy.

The structure is:
/ - root is the CMS else, not wordpress
/Anything/ - Pass to CMS controller if no other rules or file matches
/Anything/Something/ - Pass to CMS controller if no other rules or
file
matches
/wordpress/ - the real folder with wordpress files, should not be
accessed
directly
/about/ - a fake (rewrite) directory for one of the multi-sites
/blog/ a fake (rewrite) directory for one of the multi-sites

This is the Apache2 rule set for WordPress that works:

   RewriteRule ^/(wp-(content|admin|includes).*) /wordpress/$1 [L]

    # MultiSites
    RewriteRule ^/about/(wp-(content|admin|includes).*) 

/wordpress/$1
[L]
RewriteRule ^/about/(..php)$ /wordpress/$1 [L]
RewriteRule ^/about/(.
) /wordpress/index.php [L]

    RewriteRule ^/blog/(wp-(content|admin|includes).*) /wordpress/$1

[L]
RewriteRule ^/blog/(..php)$ /wordpress/$1 [L]
RewriteRule ^/blog/(.
) /wordpress/index.php [L]
RewriteRule ^/blog/tag/(.*)/ /wordpress/index.php [L]

We hard code the first slug of the URL to prevent the CMS’s regex rules
from
trying to grab it.

On the new server, with my new config, I can go to /wordpress/ and get
the
master blog, and can login to the master blog.

Some of the links want to go to a URL like /wp-admin/network/ and it
should
silently redirect to /wordpress/wp-admin/network/. That’s what the first
apache rule does. I’ve tried to create an Nginx rule:

location ^~ ^/(wp-(content|admin|includes).*) {
try_files /wordpress/$1 =404;
}

But I get a 404. So its matching the pattern but not loading the
redirected
content.

I am also having trouble getting the URL aliases to do anything. /blog/
should silently be /wordpress/blog/, so that Wordpress sees it as if it
were
/wordpress/blog/. At present, I can’t get to /wordpress/blog/ directly
or
through the alias /blog/.

I’ve been trying things like:

location ^~ ^/blog/([a-zA-Z0-9-_]+)/ {
try_files /wordpress/$1.html /wordpress/$1/;
}

… without any sucess.

I also have some pattern matching functions that insist on matching URLs
they aren’t supposed to. The ^~ is supposed to make the rule a higher
priority, but it does not work.

I have rules such as:

location ~ ^/([a-zA-Z0-9-_]+)/$ {
try_files /cache/$1.html $uri $uri/ /Director.php?rt=$1;
}

that should match any single slug, like /something/ and pass it to a
non-wordpress site UNLESS it is already in another rule, like /blog/. It
shouldn’t match /blog/ because /blog/ has its own rule that should take
priority. Right now, that is not the case and the pattern location block
is
being too greedy.

These are all the location blocks I have on the server:

index Director.php index.php;

location ^~ ^/(wp-(content|admin|includes).*) {
try_files /wordpress/$1 =404;
}

location ^~ ^/blog/([a-zA-Z0-9-_]+)/ {
try_files /wordpress/$1.html /wordpress/$1/;
}

Attempt to match Slugs for Director

location ~ ^/([a-zA-Z0-9-_]+)/$ {
try_files /cache/$1.html $uri $uri/ /Director.php?rt=$1;
}

location ~ ^/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)/$ {
try_files $uri $uri/ /Director.php?rt=$1&action=$2;
}

location / {
try_files $uri $uri/ /index.html;
}

pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

location ~ .php$ {
# Zero-day exploit defense.
# nginx 0day exploit for nginx + fastcgi PHP
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}

ERROR HANDLING

error_page 404 /404.html;

redirect server error pages to the static page /50x.html

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
       root /usr/share/nginx/html;
    }

deny access to .htaccess files

location ~ /.ht {
deny all;
}

What am I doing wrong?

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,249743,249743#msg-249743

On Tue, Apr 29, 2014 at 04:27:36PM -0400, nrahl wrote:

Hi there,

Some of the links want to go to a URL like /wp-admin/network/ and it should
silently redirect to /wordpress/wp-admin/network/. That’s what the first
apache rule does. I’ve tried to create an Nginx rule:

location ^~ ^/(wp-(content|admin|includes).*) {
try_files /wordpress/$1 =404;
}

But I get a 404. So its matching the pattern but not loading the redirected
content.

That’s not what is happening. “location ^~” is a prefix match, not a
regex match, so this location is probably not being used by anything.

I also have some pattern matching functions that insist on matching URLs
they aren’t supposed to. The ^~ is supposed to make the rule a higher
priority, but it does not work.

That’s not what ^~ means.

These are all the location blocks I have on the server:

index Director.php index.php;

location ^~ ^/(wp-(content|admin|includes).*) {

That probably won’t match any request.

location ^~ ^/blog/([a-zA-Z0-9-_]+)/ {

That probably won’t match any request.

Attempt to match Slugs for Director

location ~ ^/([a-zA-Z0-9-_]+)/$ {

That should match requests of the form /XXX/.

location ~ ^/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)/$ {

That should match requests of the form /XXX/YYY/.

location / {

That should match any request that does not otherwise match a location.

pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

location ~ .php$ {

That should match a request of the form /XXX.php.

What am I doing wrong?

Misunderstanding what “location” does?

http://nginx.org/r/location

What request do you make?

Which one of the above locations does the request match?

What output do you expect?

What output do you get?

(And what do the logs say?)

f

Francis D. [email protected]

Ok, at this point I have removed everything from the config just to try
and
get the most basic thing working.

This is the entire config now:

location ^~ /wordpress/ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location / {
return 403;
}

That’s all the location blocks.

What happens:

  1. Going to any page that does not start with /wordpress/ produces a

This is correct according to my understating of the config.

  1. Going to any url starting with /wordpress/ like /wordpress/wp-admin/
    or
    even just /wordpress/ itself, produces a blank page.

Is your blank page a http 200 with no content, or a http 200 with some
content that the browser shows as blank, or some other http response?

The blank page is a response code 200, with proper headers, but no body
HTML
at all. View page source is empty.

curl -v http://whatever/wp-admin/

Here is the output:

MyDomain is used in place of real domain.

  • Adding handle: conn: 0x18f5b00
  • Adding handle: send: 0
  • Adding handle: recv: 0
  • Curl_addHandleToPipeline: length: 1
    • Conn 0 (0x18f5b00) send_pipe: 1, recv_pipe: 0
  • About to connect() to www.MyDomain.com port 443 (#0)
  • Trying xx.xxx.xxx.xxx…
  • Connected to www.MyDomain.com (xx.xxx.xxx.xxx) port 443 (#0)

GET /wordpress/ HTTP/1.1
User-Agent: curl/7.32.0
Host: www.MyDomain.com
Accept: /

< HTTP/1.1 200 OK

  • Server nginx is not blacklisted
    < Server: nginx
    < Date: Thu, 01 May 2014 05:55:32 GMT
    < Content-Type: text/html; charset=UTF-8
    < Transfer-Encoding: chunked
    < Connection: keep-alive
    < X-Powered-By: PHP/5.5.9-1ubuntu4
    <
  • Connection #0 to host www.domain.com left intact

The logs will show which location is used. Can you see which
file-on-the-filesystem is returned?

For the request /wordpress/ with above simple config, it matches the
/wordpress/ location and passes it to fastcgi:
the log says: http upstream request: “/wordpress/?”
then: http fastcgi record length: 61
which seems a bit short. So PHP is returning nothing?

Posted at Nginx Forum:

Hi,

On Thu, 2014-05-01 at 02:07 -0400, nrahl wrote:

return 403;

MyDomain is used in place of real domain.

User-Agent: curl/7.32.0
< X-Powered-By: PHP/5.5.9-1ubuntu4
the log says: http upstream request: “/wordpress/?”
then: http fastcgi record length: 61
which seems a bit short. So PHP is returning nothing?

With wordpress MU setups, you need to manually set up the blog id
( well, at least I have so far )… here’s some extracts from one of my
site configs:

map $http_host $blogid {
default 0;
example.com.au 2;
example.com.tw 3;
}

location ~ ^/files/(.*)$ {

try_files /wp-content/blogs.dir/$blogid/$uri
/wp-includes/ms-files.php?file=$1 ;
}

and

location ^~ /blogs.dir {
internal;
alias /www/example.com/public_html/wp-content/blogs.dir ;
expires max;
}

If that still doesn’t work, can you check that you’ve got everything
connected correctly by delivering a quick <?php phpinfo(); ?> file to
your browser?

There are example configs on the net - wordpress offers one for certain,
and googling for wordpress, mu and nginx deliver a plethora of howtos.
Why not begin with one of them and try to understand it, rather than
fighting to reinvent the wheel?

Steve

Steve H. BSc(Hons) MIITP

Linkedin: http://www.linkedin.com/in/steveholdoway
Skype: sholdowa

With wordpress MU setups, you need to manually set up the blog id
( well, at least I have so far )… here’s some extracts from one of
my
site configs:

location ^~ /blogs.dir {

Thanks for sharing your config.

What version of WordPress are you running? Mine doesn’t have a blogs.dir
directory. I think they did away with that in 3.5. My WPMU setup was
working
fine without that dir on Apache, so it must not be needed in my version.

If that still doesn’t work, can you check that you’ve got everything
connected correctly by delivering a quick <?php phpinfo(); ?> file to
your browser?

I can make PHP scripts run when I disable the wordpress location blocks
and
use my CMS’s location blocks. The PHP scripts run fine, so PHP and
PHP-FPM
are running OK.

There are example configs on the net - wordpress offers one for
certain, and googling for wordpress, mu and nginx deliver a plethora of
howtos.
Why not begin with one of them and try to understand it, rather than
fighting to reinvent the wheel?

I actually did quite a lot of reading before posting here. There are
several
reasons the configs out there don’t work.

  1. WordPress Multi-Site got a major overhaul in 3.5 and the tutorials
    out
    there, including Wordpress’s own site are for the old version. I’m
    running
    3.9, which uses the new 3.5+ way of detecting blogs.

  2. My CMS uses // and //*/ (where star is any URL char) type rules to
    grab
    everything that isn’t otherwise defined explicitly, and this is creating
    problems with any smart wordpress configs. In order for this to work, I
    need
    to hard-code the wordpress blog URLs at a higher priority than the
    patterns.
    ie. if /myblog/ or /otherblog/ pass to wordpress, if /AnythingElse/
    pass
    to CMS.

  3. The CMS, not the wordpress master blog, is at the site root.

  4. The actual wordpress files live in a folder /wordpress/ which is not
    meant to be accessed directly. wordpress expects the blogs to be in
    subdirectories, such as /wordpress/someblog/, but really, we want them
    to
    appear off the root like /someblog/ so we have to trick wordpress using
    rewrite rules.

This entire configuration was 100% functional using Apache2. I’m
assuming
that Nginx can emulate any behaviour Apache can, but maybe there’s some
configs that it can’t support?

Posted at Nginx Forum:

On 01/05/14 08:02, nrahl wrote:

This entire configuration was 100% functional using Apache2.

I just saw this thread for the first time, and I am wondering if its the
same problem I hit when I moved my apache2 configuration over to nginx.
It turned out to be nothing to do with nginx, but a problem with
super-cache misplaying with php-apc.

Super-cache does something like

if(!class_exists) {
Class {

};
}

in a file called wp-cache-base.php

adding to php.ini
[apc]
apc.filters = wp-cache-base

solved the problem

The symptoms I had were - with display_errors off, php5-fpm was
returning a 500, with display_errors on php5-fpm was returning 200. When
it returned 200 there was a blank page.


Alan Chandler

On Thu, May 01, 2014 at 02:07:49AM -0400, nrahl wrote:

Hi there,

This is the entire config now:

location ^~ /wordpress/ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
}

That says “talk fastcgi to that socket”, but nothing useful is sent on
that connection because you have no fastcgi_param directives there. (And
no indication that any are inherited from the surrounding context.)

So php will not be asked to process any particular file.

You’ll probably want SCRIPT_FILENAME, and possibly some more params,
to get any kind of useful output. The details depend on your fastcgi
server, but the nginx fastcgi.conf is usually a good starting point.

location / {
return 403;
}

That’s all the location blocks.

What happens:

  1. Going to any page that does not start with /wordpress/ produces a 403.
    This is correct according to my understating of the config.

Yes, that’s what the config asks for.

  1. Going to any url starting with /wordpress/ like /wordpress/wp-admin/ or
    even just /wordpress/ itself, produces a blank page.

Yes, that’s what the config asks for.

Strictly, it provides “whatever the fastcgi server returns”; but since
that is “nothing”, that’s what you get. The fastcgi server logs may have
more details.

< X-Powered-By: PHP/5.5.9-1ubuntu4

So, nginx sent the request to the fastcgi server. That’s good.

The logs will show which location is used. Can you see which
file-on-the-filesystem is returned?

For the request /wordpress/ with above simple config, it matches the
/wordpress/ location and passes it to fastcgi:

That’s true of this config and test. The question was about the raw
wp-admin.php content that you reported with a previous config.

But it sounds like you’re progressing with getting the config to do what
you want, so that’s good.

the log says: http upstream request: “/wordpress/?”
then: http fastcgi record length: 61
which seems a bit short. So PHP is returning nothing?

Correct. PHP processes the named input file, which you haven’t set,
so it processes nothing and returns the output.

f

Francis D. [email protected]