How to configure Nginx for WordPress permalinks

I switched from Apache to Nginx web server. How do I configure permalinks under WordPress blog? How can I configure Nginx for WordPress permalinks using virtual hosting?

A permalink is nothing but the web address used to link to your blog content. The URL to each blog post or cms post should be permanent and never change. Hence the name permalink. WordPress has the ability capacity to create a custom URL structure for your blog posts and archives. There are three basic types of WordPress permalinks:

ADVERTISEMENTS

  • Ugly: https://www.cyberciti.biz/faq/?p=2284
  • Pretty using mod_rewrite: https://www.cyberciti.biz/faq/bash-for-loop/
  • Almost pretty using PHP PATHINFO: https://www.cyberciti.biz/faq/index.php/bash-for-loop/

In the Settings > Permalinks panel, you can choose one of the more common permalink structures as follows:

Fig.01: configure Nginx for WordPress permalinks : Choosing your permalink structure

Fig.01: Configure Nginx for WordPress permalinks : Choosing your permalink structure

How to configure Nginx for WordPress permalinks (“Pretty” permalinks)

Edit your nginx.conf file using a text editor such as nano command or vim command, run:
$ sudo vi /etc/nginx/site-enabled/cyberciti.biz.conf
Edit/add/append the following location block within the server block:

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

If your WordPress blog is in /faq/ sub-directory, try :

##
## note path to /faq/index.php 
##
location /faq/ {
            try_files $uri $uri/ /faq/index.php?$args; 
}

Save and close the file. Restart/reload your nginx server, run:
$ sudo systemctl reload nginx
OR
$ sudo /usr/sbin/nginx -s reload
Here is my sample config file:

# Upstream to abstract backend connection(s) for PHP.
upstream php {
  server unix:/run/php/php7.0-fpm.sock;
}
server {
    server_name www.cyberciti.biz;
    root        /var/www/html;
 
    location /faq/ {
         try_files $uri $uri/ /faq/index.php?$args;
    }
 
    #Add trailing slash to */wp-admin requests.
    rewrite /faq/wp-admin$ $scheme://$host$uri/ permanent;
 
    # Directives to send expires headers and turn off 404 error logging.
    location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|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 max;
    }
 
# Pass all .php files onto a php-fpm/php-fcgi server.
    index index.php;
    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
                return 404;
    }
# This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)
    include /etc/nginx/fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass php;
 }
}

The above config is from this site itself.

Nginx and WordPress permalinks full example

Here is config for my personal blog that runs on Ubuntu Linux 16.04 LTS along with PHP 7.x.x:

## Upstream to abstract backend connection(s) for PHP ##
upstream php {
  server unix:/run/php/php7.0-fpm.sock;
}
## my server ip address ##
server {
    set_real_ip_from  10.147.164.1;
    real_ip_header    X-Forwarded-For;
    listen 10.147.164.3:80;
    server_name theos.in www.theos.in;
 
    root        /home/lighttpd/theos.in/http;
 
    ## WordPress Perm links config ##               
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
 
    ## Add trailing slash to */wp-admin requests.
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;
 
    ## Deal with sitemap wordpress plugin urls ##
    rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml$ "/index.php?xml_sitemap=params=$2" last;
    rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml\.gz$ "/index.php?xml_sitemap=params=$2;zip=true" last;
    rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html$ "/index.php?xml_sitemap=params=$2;html=true" last;
    rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html.gz$ "/index.php?xml_sitemap=params=$2;html=true;zip=true" last;
 
    # Directives to send expires headers and turn off 404 error logging.
    location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|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 max;
    }
 
   ## Okay, Pass all .php files onto a php-fpm/php-fcgi server.
    index index.php;
    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
                return 404;
    }
   ## Setting works on Ubuntu/Debian Linux  
   ### This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)
    include /etc/nginx/fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass php;
 }
}
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04

ADVERTISEMENTS
2 comments… add one
  • greg Feb 5, 2017 @ 10:13

    Greetings
    I’m struggling with a similar problem, and wasn’t able to make it relative: it’s working if the installation is at the root, but not if it’s inside a folder, unless you specify a path explicitly. It seems to be a case where Apache is still more flexible.

  • Dani Sep 12, 2017 @ 18:30

    Doesn’t work here.

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.