Permalink Wordpress on Nginx

Discussion in 'Tips/Tricks/Mods' started by DavideR, Oct 3, 2023.

  1. DavideR

    DavideR Member HowtoForge Supporter

    Hi, I have a problem with Wordpress permalinks on Nginx:
    custom structure gives error. How can I control mod_rewrite on Nginx with ISP Config 3.2.10?
    Thanks.
     
  2. till

    till Super Moderator Staff Member ISPConfig Developer

    Add this to the nginx directives field on the options tab of the website:

    Code:
    location / {
                    try_files $uri $uri/ /index.php?$args;
           }
    
           # Add trailing slash to */wp-admin requests.
           rewrite /wp-admin$ $scheme://$host$uri/ permanent;
    
           location ~*  \.(jpg|jpeg|png|gif|css|js|ico)$ {
                    expires max;
                    log_not_found off;
           }
    Btw. This is copied from the first result that you get when you search at Google for "ISPConfig WordPress nginx" :)
     
    Gwyneth Llewelyn and ahrasis like this.
  3. ;)

    For a very basic WordPress setup, I also add the following at the end of Till's directives, just for that tiny extra bit of security:

    Code:
    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
    }
    But that's not all. You will almost certainly want some sort of cache working with WordPress. While almost all caches (at least, the 4-5 I have used :D ) will fall back to PHP, they benefit the most if you allow nginx to serve the static files directly from disk, and not requesting it via PHP. For example, this is what I use with WP Fastest Cache (free version available on the WordPress Plugin Library):

    Code:
    # configuration directives to support WP Fastest Cache plugin.
    # note not all features are supported.
    
    # default location block
    # - directs mobile visitors to @mobileaccess, if configured.
    # - directs cache misses to PHP (via @cachemiss).
    # - directs requests "that shouldn't be cached" to PHP (via @cachemiss): example - requests from a logged-in user.
    location / {
        error_page 418 = @cachemiss; # to handle cache misses
        error_page 419 = @mobileaccess; # to handle mobile visits
        recursive_error_pages on;
    
        # bypass POST requests
        if ($request_method = POST) { return 418; }
    
        # uncommenting the following degrades the performance on certain sites. YMMV
        # if ($query_string != "") { return 418; }
    
        # bypass cache for common query strings
        if ($arg_s != "") { return 418; } # search query
        if ($arg_p != "") { return 418; } # request a post / page by ID
        if ($args ~ "amp") { return 418; } # amp test
        if ($arg_preview = "true") { return 418; } # preview post / page
        if ($arg_ao_noptimize != "") { return 418; } # support for Autoptimize plugin
    
        # if WP related cookies are found, skip cache
        if ($http_cookie ~* "wordpress_logged_in_") { return 418; }
        if ($http_cookie ~* "comment_author_") { return 418; }
        if ($http_cookie ~* "wp_postpass_") { return 418; }
    
        # avoid duplicate content on Amazon CloudFront and KeyCDN.
        if ( $http_user_agent = "Amazon CloudFront" ) { return 403; access_log off; }
        if ($http_x_pull = "KeyCDN") { return 403; access_log off; }
    
        # uncomment the following, if WP Fastest Cache plugin is set to create a separate cache for mobile visitors
        # if ( $http_user_agent ~* "2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800|iPad" ) { return 419; }
        # add_header "Vary" "User-Agent";
    
        # uncomment the following if deemed fit, in addition to the above line to enable @mobileaccess
        # if ( $http_user_agent ~* "w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-|ipad" ) { return 419; }
    
        # look for cached version; if-not-found, then send the request to PHP
        try_files "/wp-content/cache/all/${uri}index.html" "/wp-content/cache/all/${uri}/index.html" $uri $uri/ /index.php$is_args$args;
    
        #--> all the following would apply, only if the request hits the cache
    
        # add some useful headers
        add_header "X-Cache" "HIT - WP Fastest Cache";
        add_header "X-CF-Powered-By" "WP Fastest Cache";
        add_header "Vary" "Cookie";
        # include "conf.d/hsts.conf";
        include 'conf.d/security-headers.conf';
    
        expires 30m;
        # expires modified 30m;
        add_header "Cache-Control" "must-revalidate";
    
        # For proxies
        # add_header "Cache-Control" "s-maxage=600";
    }
    
    # location to handle requests come from mobile devices
    location @mobileaccess {
        # look for cached version for mobiles; if-not-found, then send the request to PHP
        try_files "/wp-content/cache/wpfc-mobile-cache/${uri}index.html" "/wp-content/cache/wpfc-mobile-cache/${uri}/index.html" $uri $uri/ /index.php$is_args$args;
    
        #--> all the following would apply, only if the request hits the cache
    
        # add some useful headers
        add_header "X-Cache" "HIT - Mobile - WP Fastest Cache";
        add_header "Vary" "User-Agent, Cookie";
        # include "conf.d/hsts.conf";
        include 'conf.d/security-headers.conf';
    
        expires 30m;
        # expires modified 30m;
        add_header "Cache-Control" "must-revalidate";
    
        # For proxies
        # add_header "Cache-Control" "s-maxage=600";
    }
    
    location @cachemiss {
        # on cache miss, send the request to PHP
        try_files $uri $uri/ /index.php$is_args$args;
    }
    For the code above to work "as is", I would also recommend you to add the following security headers on a file named /etc/nginx/conf.d/security-headers.conf with something like the following:

    Code:
    add_header X-Content-Type-Options nosniff;
    
    # please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
    # add_header X-Frame-Options deny;
    add_header X-Frame-Options SAMEORIGIN;
    
    add_header X-XSS-Protection "1; mode=block";
    
    add_header Referrer-Policy "no-referrer-when-downgrade";
    
    # optional header - use it with care - you are warned!
    # add_header Access-Control-Allow-Origin "*";
    A comment on the above: if you're familiar with nginx, then you know that If is Evil, and this configuration has a lot of "ifs". However, "if" is fine if the only thing you have inside the if clause is... a return or rewrite. This is essentially the case in the above configuration. Nevertheless, use it with care — I can't help you much, since this wasn't written by me, just shamelessly copied from elsewhere and tweaked for my purposes.

    If you have a different caching plugin... well, most will have their own suggested nginx configuration. If not... stay away from it :) It means that nobody has tested that plugin with nginx, and even if it might work, it might not have a dramatic performance impact. And if you're using nginx, that's what you want, right? :)

    One possible alternative with nginx is... not to use a caching plugin at all! Instead, rely upon something known as FastCGI Cache. Unlike Apache, which can include PHP as one of its many modules, nginx only serves static content — fast. That said, all PHP code is handled instead by PHP-FPM, running separately. Nginx hands the PHP script to PHP-FPM for processing, PHP-FPM interprets or compiles just-in-time the PHP code, talks to the database, and produces a static HTML page — which it hands back to nginx. Now that it's a static page, nginx can serve it to the client.

    What FastCGI Caching does is simply to grab a copy of that HTML-generated page and save it on disk. The next time a request comes from that page, it's already on disk. Nginx doesn't need to contact PHP-FPM at all. In other words: why bother to have a caching plugin that runs the whole WordPress code and produces a static page that is archived on disk... when PHP-FPM already does that page? It just doesn't save it — that's the job of FastCGI Caching.

    The problem is that when WordPress runs inside PHP-FPM, many things can happen. MySQL might be slow in responding (or not at all). There might be errors here or there. There is the issue of handling things like cookies and logged in users. Nginx knows nothing about any of those — and doesn't really care, since that's the job for PHP-FPM. The best that it can do is the following: a new request comes in. Nginx sees if it already has a cached page for it. It does. Is this page fresh? Well, probably not. So nginx asks PHP-FPM for a newer version. But PHP-FPM is having problems in rendering a new page... because MySQL is choking, or it's out of memory, or something. No problem — nginx takes over and feeds the user a stale — but perfectly working! — page. Instantly. So now you not only can serve pages super-quickly, but even can serve pages when the database server is down!

    Of course, caching plugins can do much more than just pre-generating static HTML, so these might still be better than FastCGI Caching (some benchmarks I saw showed that the difference is not very significative on a "test" scenario — i.e. using the synthetic Apache Benchmark — but who knows what happens on a "real world" test...). But if you want to give FastCGI Caching a try, here is a good tutorial to follow: https://www.linode.com/docs/guides/how-to-use-nginx-fastcgi-page-cache-with-wordpress/

    Good luck!
     
  4. slagroom

    slagroom Member

    And I would add to all this that it works wonders if you put that static content in RAM. So your cached static content will be served instantly from DDR. For that purpose you may use things like log2ram or other tools that create/force-use ramdisks for you. Yes, linux already uses tmpfs and caches files from RAM, but my experience is that this part of linux doesn't work too well in real world application, not even in 2023. RAM-disks still work wonders, if you have room available for it in your total RAM. I have even tested serving full web doc-roots (so including php files) from RAM, and that too causes drastic performance increases.
    Just remember, random searching, accessing and reading data from DDR3 already easily reaches around 7 GB/sec bi-directional. Speeds not even approached by the average NMVe RAID-10 yet. Don't let people fool you into believing RAM speeds "have been beaten by PCIe drives", they haven't. You'll always have controller-lag and cache to go through, which isn't there for RAM.
     
    hadizeid likes this.

Share This Page