Synology NGINX WordPress Permalinks

Fix those 404 Permalink errors!

* This is an updated version of my previous post where I explained how to setup Permalinks on your Synology WordPress site. I had to recently factory reset my NAS and found a much easier way.

So I moved my web hosting to a personal NAS a few months ago. I’m using a Synology DS to serve up this sweet data to you from the warmth of my home. The WebStation defaults to the NGINX server with the option for Apache. I’ve heard good things about NGINX speeding up sites like mine so I opted out of Apache. And things went great for a while… until I wanted to turn on Permalinks. NGINX does not support .htaccess files and doesn’t work with Permalinks by default. But, I had two reasons for perusing this; to make my site look pretty, and to use a cache plugin that required Permalinks.

I’ll go through my process of learning and getting everything working. For those who enjoy my pain and want to follow along. The final results and solution will be at the bottom of the page here.



My Progress

So, first off was finding out how to access my NGINX configuration on this NAS. I logged into SSH using my Windows Terminal.

ssh 192.168.1.xx -p xx

After I was there, I found the ‘nginx.conf’ configuration file by going to ‘/etc/nginx/’.

#Your location may be different.
cd /etc/nginx

-SSH Text Editing-

Next was a short learning curve… learning how to use the VIM text editor in SSH. Grant it; I’ve never used a text editor in SSH beforem. I’m sure you can look here if you want to learn about more commands. I’ll go through the ones I used.

#Opens or creates a file using VIM.
sudo vi nginx.conf

Watch the bottom-left corner of the window. You’ll see “– REPLACE –“. Press the Insert key to switch to “– INSERT –” mode for easier typing.

Pressing Esc brings you to a command mode. You then press “:” to enter a command.

  • “:q” “Enter” Quits
  • “:q!” “Enter” Quits w/o saving
  • “:w” “Enter” Saves

Commands without “:”.

  • “gg” “Enter” Goes to the start of the file
  • Capital “G” “Enter” Goes to the end of the file
  • “dG” “Enter” Deletes from current position to the end of the file

You can highlight with your mouse and use Ctrl+C to copy. You can right click your mouse to paste.




-Initial Attempt- (#learning)

After searching for some time through my NAS and finding the configuration file, I had my vague internet instructions to work with. I’ll list just a few sites I’ve searched through here:

I needed to add a snippet of code to my config file to redirect traffic back to the main ‘index.php’ if no other index file is found. Sounds easy enough. After studying the layout of the config file, it’s layed out as such:

#Brief layout.
http {
   server {
      listen 80 default_server;
      location {
      }
      location {
      }
   }
   server {
      listen 443 default_server;
      location {
      }
   }
}

There are multiple ‘server’ headings that use a ‘listen’ option to allow NGINX to specify what ‘server’ heading to use with what port. If there are multiple ‘server’ entries with the same port, then NGINX will use the one labeled ‘default_server’. Each ‘location’ listed tells the server what to do or how to redirect specific locations or url strings. You can have multiple ‘location’ entries as long as they are all looking for something different.

This is the server location entry that NGINX needs for WordPress:

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

So I attempted to insert the code to update my site. This is the actual ‘server’ entry portion of my config:

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

    gzip on;

    server_name _;

    location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
        internal;

        root /;

        open_file_cache off;

        include app.d/x-accel.*.conf;
        include conf.d/x-accel.*.conf;
    }

    include app.d/www.*.conf;
    include app.d/alias.*.conf;
    include /usr/syno/share/nginx/conf.d/www.*.conf;
    include conf.d/www.*.conf;

    location = /webdefault/images/logo.jpg {
        alias /usr/syno/share/nginx/logo.jpg;
    }

    error_page 403 404 500 502 503 504 @error_page;

    location @error_page {
        root /usr/syno/share/nginx;
        rewrite (.*) /error.html break;
        allow all;
    }

    location ^~ /.well-known/acme-challenge {
        root /var/lib/letsencrypt;
        default_type text/plain;
    }

    include app.d/.location.webstation.conf*;

    ##Replaced this location section
    #location / {
    #    rewrite ^ / redirect;
    #}

    ##With this one:
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ ^/$ {
        rewrite / https://$host:2001/ redirect;
    }
}

I then restarted my NGINX service.

#Check for errors
sudo nginx -s reload

#Restart server
sudo synoservice --restart nginx

Nothing happened. I found that, upon restart, NGINX resets the main configuration file! Not even locking the file as Read Only prevented this.



-More Research and Testing-

So the sections within the nginx.conf file marked ‘Include’ uses script from whatever file is being included. I found multiple locations online stating that I need to make a separate configuration file in another folder. This config will be included with the main config when the script is ran.

I attempted to create a new .conf file under the ‘sites-enabled’ folder. This option was valid and my ‘location’ entry was getting added to the configuration. However, the server skipped my entries in favor for it’s own marked “default_server”. No matter what I tried, or what location I placed my new .conf file, my ‘location’ entry never got used.

I also attempted to add the location block to other .conf files that are included with the server block. This only resulted in duplicate ‘location’ block errors since it already contained a location block for “/”.

After many hours of research with much trial and error, I gave up this path.



-Final Attempt-

-Edit: Hackish Workaround-

Eventually, I decided to try adding another port to my NGINX server in the UI settings. This way, I could add a new ‘server’ section with my new port.

I use SSL on my site. So I added a third port marked as SSL (separate from the original SSL port). Once I did this, I revisited the last option above: I created a new configuration file under ‘sites-enabled’. Here, I created a duplicate ‘server’ tag as the original SSL Port 443, but replaced the port number with my new SSL port. The server no longer seen this as a duplicate entry and allowed my modified ‘location’ entry to be used.

Configuration saved as “/etc/nginx/sites-enabled/pcaffinity.com.conf”:

server {
    ##This allowed me to set my own default_server
    listen 444 default_server ssl;

    gzip on;

    server_name pcaffinity.com *.pcaffinity.com;

    location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
        internal;

        root /;

        open_file_cache off;

        include app.d/x-accel.*.conf;
        include conf.d/x-accel.*.conf;
    }

    include app.d/www.*.conf;
    include app.d/alias.*.conf;
    include /usr/syno/share/nginx/conf.d/www.*.conf;
    include conf.d/www.*.conf;

    location = /webdefault/images/logo.jpg {
        alias /usr/syno/share/nginx/logo.jpg;
    }

    error_page 403 404 500 502 503 504 @error_page;

    location @error_page {
        root /usr/syno/share/nginx;
        rewrite (.*) /error.html break;
        allow all;
    }

    location ^~ /.well-known/acme-challenge {
        root /var/lib/letsencrypt;
        default_type text/plain;
    }

    include app.d/.location.webstation.conf*;

    ##This is the secret sauce
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

}

This worked, but it was far from ideal.



-Update: A Better Tomorrow-

Ok, so I stumbled upon a new configuration file when I was factory resetting my NAS.

/etc/nginx/app.d/server.webstation-vhost.conf

server {

    listen      80;
    listen      [::]:80;

    listen      443 ssl http2;
    listen      [::]:443 ssl http2;

    server_name pcaffinity.com;

    ssl_certificate             /usr/local/etc/certificate/WebStation/vhost_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/fullchain.pem;
    ssl_certificate_key         /usr/local/etc/certificate/WebStation/vhost_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/privkey.pem;
    include /usr/syno/etc/security-profile/tls-profile/config/vhost_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.conf*;

    add_header  Strict-Transport-Security max-age=15768000;
    ssl_prefer_server_ciphers   on;

    location ^~ /.well-known/acme-challenge {
        root /var/lib/letsencrypt;
        default_type text/plain;
    }

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

    root    "/volume1/web";
    index    index.html  index.htm  index.cgi  index.php  index.php5 ;
    error_page 400 401 402 403 404 405 406 407 408 500 501 502 503 504 505 @error_page;

    location @error_page {
        root /var/packages/WebStation/target/error_page;
        rewrite ^ /$status.html break;
    }

    location ^~ /_webstation_/ {
        alias    /var/packages/WebStation/target/error_page/;
    }

    location ~* \.(php[345]?|phtml)$ {
        fastcgi_pass unix:/run/php-fpm/php-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.sock;
        fastcgi_param HOST "pcaffinity.com";
        include fastcgi.conf;
    }

    include /usr/local/etc/nginx/conf.d/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/user.conf*;
	
}

This file has a combined ‘server’ block for your VirtualHost. The best part was that it did not already contain the ‘location’ block that I needed. It also has an ‘include’ statement pointing to a file that was not on my NAS.

/etc/nginx/conf.d/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/user.conf

I created a new file in the directory that configuration listed. I then simply added the ‘location’ block to that user.conf.

  • Step 1 – Open the vhost.conf file and find the Include location.
    • Note that it may be the only folder beneath “conf.d”.
sudo vi /etc/nginx/app.d/server.webstation-vhost.conf
  • Step 2 – Close the file without saving.
{Esc} :q! {Enter}
  • Step 3 – Create a new file using the ‘include’ location.
    • This will open the file if it already exists.
sudo vi /etc/nginx/conf.d/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/user.conf
  • Step 4 – Paste your location code into the file.
    • (Text editor instructions found above)
#Code added to user.conf
location / {
   try_files $uri $uri/ /index.php?$args;
}
  • Step 5 – Save and close.
{Esc} :wq {Enter}

I hope this helps someone out there. Too many hours were lost on this NGINX Synology WordPress Permalinks issue.




Content Security Policy

I just wanted to add a side note here. If you plan on using Content-Security-Policy on your site, this would be the location to edit. I have successfully updated my CSP in my ‘user.conf’ file. Here is the code that I’ve added:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: https://ssl.google-analytics.com https://*.facebook.net https://*.facebook.com https://www.powr.io https://secure.gravatar.com https://*.wp.com https://*.twitter.com; img-src 'self' data: https://i.udemycdn.com https://*.wordpress.com https://*.wp.com https://*.google-analytics.com https://*.facebook.com https://udemy-certificate.s3.amazonaws.com http://null https://secure.gravatar.com https://*.hardenize.com https://www.powr.io; style-src-elem 'self' 'unsafe-inline' 'unsafe-eval' data: https://secure.gravatar.com https://*.google.com https://*.googleapis.com https://*.wp.com; style-src 'self' 'unsafe-inline' data: https://secure.gravatar.com https://*.google.com https://*.googleapis.com https://*.wp.com; font-src 'self' data: https://themes.googleusercontent.com https://fonts.googleapis.com https://fonts.gstatic.com https://*.wp.com https://*.wordpress.com https://wordpress.com; frame-src 'self' https://*.facebook.com https://*.wp.com https://www.powr.io https://*.wordpress.com; object-src 'none'; connect-src 'self' https://*.google.com https://*.googleapis.com https://yoast.com;";

CSP is ugly, but it works. You can learn more here if you’d like. (Side note to this side note- Try not to use ‘unsafe-inline’ if you can help it.)




Updating Permalink Settings

Now that your server is finally setup, be sure to make the permalink changes you’ve always wanted. Though if you’ve gone through the trouble above, you should already know this part.

  • Choose ‘Settings->Permalinks’ from your WordPress Dashboard.
  • Choose the option that best fits your site.
    • I chose a custom Permalink using ‘/%category%/%postname%/’.
  • Lastly, Save changes. That’s it!

As long as your server settings are correct, everything should automagically work.