Nginx — disable aggressive caching for WordPress admin areas

cachenginxWordpress

I have a fully Nginx install now, with PHP-FPM. Coming from a world of Apache with Nginx proxying in front.

It seems Nginx has its own moody caching, very aggressive. There are a few layers of caching:

  1. Nginx's fastcgi cache itself. In my nginx.conf I have the following settings:

    fastcgi_cache_path        /var/run/nginx-cache  levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
    fastcgi_cache_key         "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale   error timeout invalid_header http_500;
    fastcgi_ignore_headers    Cache-Control Expires Set-Cookie;
    
  2. THen there's php opcache. I've disabled it for now:

    ;zend_extension=opcache.so
    opcache.enable=0
    opcache.enable_cli=0
    opcache.memory_consumption=250
    opcache.interned_strings_buffer=8
    opcache.max_accelerated_files=6000
    opcache.revalidate_freq=600
    opcache.fast_shutdown=1
    
  3. I also have the following skip cache directives in the server blocks:

    set $skip_cache 0;
    
    # POST requests and urls with a query string should always go to PHP
    if ($request_method = POST) {
            set $skip_cache 1;
    }
    if ($query_string != "") {
            set $skip_cache 1;
    }
    
    #Don't cache the following URLs
    if ($request_uri ~* "/(wp-login.php|wp-admin|login.php|backend|admin)"){
        set $skip_cache 1;
    }
    
    #Don't cache if there is a cookie called PHPSESSID
    if ($http_cookie ~* "PHPSESSID"){
        set $skip_cache 1;
    }
    
    #Don't cache if there is a cookie called wordpress_logged_in_[hash]
    if ($http_cookie ~* "wordpress_logged_in_"){
        set $skip_cache 1;
    }
    
    # Don't cache uris containing the following segments
    if ($request_uri ~* "cms/|/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
            set $skip_cache 1;
    }
    
    # For the arc 
    if ($request_uri ~* "site/|/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
            set $skip_cache 1;
    }
    
    # Don't use the cache for logged in users or recent commenters
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
            set $skip_cache 1;
    }
    
  4. Then in the PHP block, I use that skip_cache stuff:

    location ~ \.php$ {
    
          try_files $uri $uri/ =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
    
            fastcgi_cache_bypass                $skip_cache;
          fastcgi_no_cache                    $skip_cache;
            fastcgi_cache                       WORDPRESS;
            fastcgi_cache_valid  200 301 302    60m;
    }
    

Despite all this, my admin screens are so aggressively cached that when I activate a plugin, it shows the older screen. When the plugin was deactivated. Only when I manually refresh the page do I see that the plugin is indeed activated.

What am I missing?

Best Answer

The way @MichaelHampton suggested is IMHO best (easiest, most efficient), but here's an alternative / additional approach that may be useful in some situations. It comes from my Wordpress / Nginx tutorial. Some themes mess with caching headers in a really bad way, so I basically want to rewrite all the headers differently for different areas of the website. I also remove the old Pragma header.

I define blocks for specific URLs or subdirectories so I can rate limit or control headers more precisely.

Note that "more_clear_headers" and "add_header" are part of the Headers More Nginx extension. If you look at the tutorial I linked to above, part two, it gives step by step instructions how to build Nginx with that module - it's pretty easy.

# Rate limiting for login pages
limit_req_zone $binary_remote_addr zone=login:1m rate=5r/m;

# Rate limit wp-login.php to help prevent brute force attacks
location = /blog/wp-login.php {
  # Next line applies the rate limit defined above
  limit_req zone=login burst=3;       
  fastcgi_keep_conn on;
  fastcgi_intercept_errors on;
  fastcgi_pass   php;
  include        fastcgi_params;
  fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
  more_clear_headers "Cache-Control";
  more_clear_headers Server; more_clear_headers "Pragma"; more_clear_headers "Expires";

  # No caching
  more_clear_headers "Cache-Control";
  add_header Cache-Control "private, max-age=0, no-cache, no-store";
  more_clear_headers "Expires";

  # Debugging aid - remove
  # add_header Z_LOCATION "WP-LOGIN"; add_header URI $uri;
}

# Wordpress admin caching headers are generally set correctly, for pages and resources. The only reason we define
# this block separately is to avoid messing with the headers in the main php block.
# This is probably unnecessary because of the skip_cache variable and may be removed
location ~* wp-admin {
  fastcgi_keep_conn on;
  fastcgi_intercept_errors on;
  fastcgi_pass   php;
  include        fastcgi_params;
  fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

  # Debugging aid - remove
  # add_header Z_LOCATION "WP_ADMIN"; add_header URI $uri; add_header "Z_SKIP_CACHE" $skip_cache;
}

To add something else to add to Michael's answer, I also use this block. It disables caching for Wordpress Admin in a second way, and also disables caching for feeds, sitemap, xmlrpc, and a few others. Part of it's for Wordpress, I suspect part of it's for a custom written PHP website I run.

if ($request_uri ~* "/wp-admin/|/admin-*|/purge*|/xmlrpc.php|wp-.*.php|/feed/|sitemap(_index)?.xml|wp-cron") {
  set $fastcgi_nocache "true";
}