Anyone got WordPress Multisite using subdirectories working on NginX - with each site serving to https only?

February 12, 2018 3k views
Nginx WordPress Let's Encrypt Ubuntu 16.04

I’ve run up my old Ubuntu 12.04 WordPress Network site using Apache to a brand new 16.04 Nginx instance. The main site is working fine, but I can’t get it to serve any of the subdirectory site contents in my network. I’ve installed the NginX Helper plugin, have Lets Encrypt Certs in place for the main site and the first of 7 subsites, but all I get is permission errors or the NginX welcome to your new website page.

Any ideas what i’m doing wrong?

Sites-available file as below.

Ian W.

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

#map $http_host $blogid {
#    default 0;
#    include /var/www/html/wp-content/uploads/nginx-helper/map.conf;
#}

map $uri $blogname{
    ~^(?P<blogpath>/[^/]+/)files/(.*)       $blogpath ;
}

map $blogname $blogid{
    default -999;
    include /var/www/html/wp-content/uploads/nginx-helper/map.conf;
}

#
# Default server configuration
#

server {
    listen 80;
    listen 443 ssl http2;

    server_name software-enabled.com www.software-enabled.com 139.59.187.99;

    if ( $scheme = "http") {
        return 301 https://$server_name$request_uri;
    }

    root /var/www/html;
    index index.php index.html index.htm index.nginx-debian.html;

    # ESSENTIAL : no favicon logs
    location = /favicon.ico { log_not_found off; access_log off; }

    # ESSENTIAL : robots.txt
    location = /robots.txt { log_not_found off; access_log off; allow all; }

    # ESSENTIAL : Configure 404 Pages
    error_page 404 /404.html;

    # ESSENTIAL : Configure 50x Pages
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    # PERFORMANCE : Set expires headers for static files and turn off logging.
    location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|r
ss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav
|bmp|rtf)$ {
        access_log off; log_not_found off; expires 30d;
    }

    # SECURITY : Deny all attempts to access PHP Files in the uploads directory
    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
    }

    #
    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }

    # Nginx Rewrite rules attempt by Ian W

    location / {
    try_files $uri $uri/ /index.php?$args;
    }

    location ~ ^/files/(.*)$ {
        try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?f
ile=$1 ;
         access_log off; log_not_found off; expires max;
        }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
     expires 24h;
     log_not_found off;
        }

    location ^~ /blogs.dir {
     internal;
     alias /var/www/html/wp-content/blogs.dir ;
     access_log off; log_not_found off;      expires max;
        }

    if (!-e $request_filename) {
     rewrite /wp-admin$ $scheme://$host$uri/ permanent;
     rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
     rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
        }

    # End of Rewrite Code!

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny all;
    }

    access_log  /var/log/nginx/$host-access.log;
    error_log   /var/log/nginx/wpms-error.log;

    ssl_certificate /etc/letsencrypt/live/software-enabled.com/fullchain.pem; # 
managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/software-enabled.com/privkey.pem; 
# managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

#
# Virtual Host configuration for ianwaring.com/www.ianwaring.com
#
server {
    listen 80;
    listen 443 ssl http2;

    server_name ianwaring.com www.ianwaring.com;

    if ( $scheme = "http") {
        return 301 https://$server_name$request_uri;
    }

#    root /ianwaring/;
#    index index.php;

    location ~ ^(/[^/]+/)?files/(?<rt_file>.+) {
    try_files /wp-content/blogs.dir/$blogid/files/$rt_file /wp-includes/ms-f
iles.php?file=$rt_file ;
    access_log off; log_not_found off; expires max;
    }

#    location / {
#      try_files $uri $uri/ /index.php?$args;
#    }

#    location ~ ^/files/(.*)$ {
#      try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?fi
le=$1 ;
#      access_log off; log_not_found off; expires max;
#    }

#    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
#   expires 24h;
#   log_not_found off;
#    }

#   location ^~ /blogs.dir {
#   internal;
#   alias /var/www/html/wp-content/blogs.dir ;
#   access_log off; log_not_found off;      expires max;
#    }

    if (!-e $request_filename) {
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;
    rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
    rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
    }

    access_log  /var/log/nginx/$host-access.log;
    error_log   /var/log/nginx/wpms-error.log;

    ssl_certificate /etc/letsencrypt/live/ianwaring.com/fullchain.pem; # managed
 by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ianwaring.com/privkey.pem; # manag
ed by Certbot

}
5 Answers
jOte February 12, 2018
Accepted Answer

/sites-available/www.somesite.com

server {
  listen 80;
  server_tokens off;

  server_name www.somesite.com somesite.com;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  server_tokens off;

  server_name www.somesite.com somesite.com;

  ssl_protocols TLSv1.2;
  ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
  ssl_prefer_server_ciphers On;

  ssl_certificate /etc/letsencrypt/live/www.somesite.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/www.somesite.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/www.somesite.com/chain.pem;

  ssl_session_cache shared:SSL:128m;
  add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
  #ssl_stapling on;
  #ssl_stapling_verify on;

  root /var/www/html/www.somesite.com;
  index index.php index.html;

  access_log /var/log/nginx/somesite_access.log;
  error_log /var/log/nginx/somesite_error.log;
  include /etc/nginx/global/wordpress.conf;
}

wordpress.conf

# WordPress multisite subdirectory rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
  try_files $uri $uri/ /index.php?$args;
}

# Directives to send expires headers and turn off 404 error logging.
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
  expires 24h;
  log_not_found off;
}

# location ~ ^/[_0-9a-zA-Z-]+/files/(.*)$ {
#   try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ;
#   access_log off; log_not_found off; expires max;
# }

#avoid php readfile()
location ^~ /blogs.dir {
  internal;
  alias /var/www/example.com/htdocs/wp-content/blogs.dir ;
  access_log off; log_not_found off;      expires max;
}

# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-ms-subdir-wp-super-cache.conf;
#include global/wordpress-ms-subdir-w3-total-cache.conf;

# Rewrite multisite '…/wp-.*' and '…/*.php'.
if (!-e $request_filename) {
  rewrite /wp-admin$ $scheme://$host$uri/ permanent;
  rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
  rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
}

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
  # Zero-day exploit defense.
  # http://forum.nginx.org/read.php?2,88845,page=3
  # Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
  # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine.  And then cross your fingers that you won't get hacked.
  try_files $uri =404;

  fastcgi_split_path_info ^(.+\.php)(/.+)$;
  #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

  include fastcgi_params;
  fastcgi_read_timeout 600;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_intercept_errors on;
  fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}


Lovely, thank you.

I’m wondering if that wordpress.conf is handling all the multisite subdirectories and using the main cert only for https: , or if I have to do one server block per multisite domain name (with the Wordpress.conf file alongside each) and put the matching cert in each.

In my case, software-enabled.com/www.software-enabled.com is the main domain, but the multisites are in software-enabled.com/ianwaring (for ianwaring.com/www.ianwaring.com), software-enabled.com/obesemanrowing (for www.obesemanrowing.org.uk/obesemanrowing.org.uk), etc, etc.

I’m probably over thinking it - need to get out for a short walk :-)

Cant see how that helps unfortunately…

Found this thread while I was trying to accomplish the very same. After a lot of trial and error I ended up getting my Wordpress Subdirectory Multisite to work with multiple https domains. Also just for the record I’m running Ubuntu 16.04

I’m far from an expert on NginX, but I’ll share what worked for me.

To start I -

  • pointed both of the domains I wanted to use to my droplet
  • installed wordpress and set up my primary and sub-site
  • issued separate SSL certificates via certbot/letsencrypt ($ certbot –nginx -d example.com -d www.example.com) - I did this for each domain, so in total I had two separate certs.
  • In the WP Network Admin -> Sites, I edited the Site Address for my sub-site to point to it’s own domain (https://subdirectorydomain.com vs. the default https://primarydomain.com/subdirectorydomain)

In retrospect I think I could have left out the –nginx parameter in the certbot command and possibly used certonly since I ended up deleting or reorganizing a bunch of what got auto put in by certbot. However I’m not entirely certain what all including the –nginx does so maybe to be safe leave it in.

Then in my etc/nginx/sites-available folder I have three files which are all sym-linked in sites-enabled

  • default
  • primarydomain.com
  • subdirectorydomain.com

default looks like this

server {
    server_name _;

    root /var/www/html;

    index index.php;

    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$uri/ permanent;
        rewrite ^(?!^/my-db-admin)(/[^/]+)?(/wp-.*) $2 last;
        rewrite ^(?!^/my-db-admin)(/[^/]+)?(/.*\.php) $2 last;
    }

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

primarydomain.com

server {
    listen [::]:80;
    listen 80;
    server_name primarydomain.com www.primarydomain.com;

    return 301 https://$host$request_uri;

}

server {
    listen [::]:443 ssl;
    listen 443 ssl;

    server_name primarydomain.com www.primarydomain.com;

    root /var/www/html;
    index index.php;

    ssl_certificate /etc/letsencrypt/live/primarydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/primarydomain.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

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

    location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}

subdirectorydomain.com

server {
    listen [::]:80;
    listen 80;
    server_name subdirectorydomain.com www.subdirectorydomain.com;

    return 301 https://$host$request_uri;

}

server {
    listen [::]:443 ssl;
    listen 443 ssl;

    server_name subdirectorydomain.com www.subdirectorydomain.com;

    root /var/www/html;
    index index.php;

    ssl_certificate /etc/letsencrypt/live/subdirectorydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/subdirectorydomain.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

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

    location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}

Hope that helps someone. Also if anyone has any suggestions about cleaning up or adding to my server blocks I’d be interested.

Have another answer? Share your knowledge.