• Resolved Rodrigo Gomes

    (@rodrigogomes-1)


    I’m trying to create a simple plugin that will add a custom header if defined('DONOTCACHEPAGE') is true.

    Like this:

    function my_function() {
            if(defined('DONOTCACHEPAGE')) {
                    header("MyCustomHeader: 12345");
            }
        }
    add_action( 'init', 'my_function', 9999 );

    But this does not work as I expected! And I don’t understand why. I’ve tried even simpler:

    if(defined('DONOTCACHEPAGE')) {
        header("MyCustomHeader: 12345");
    }

    I’m using the Woocommerce plugin to test. This plugin defines DONOTCACHEPAGE as TRUE in the cart, my account, etc.

    If I set define('DONOTCACHEPAGE', true) in the configuration file, the plugin works as expected. However, I want it to work with other plugins that define this information, such as WooCommerce and Ninja Form.

    Please someone help me to see what I’m missing!

Viewing 7 replies - 1 through 7 (of 7 total)
  • Moderator bcworkz

    (@bcworkz)

    You might have a “Catch-22” situation here. You are basically trying to send too early. You need to use the right action from which to output your header. After DONOTCACHEPAGE has been defined, but before any output has started. It may not be possible to use the right action for any random plugin that defines DONOTCACHEPAGE since we’ve no idea when it might be defined. It may be that in some cases DONOTCACHEPAGE is defined after output has started, so the situation becomes impossible.

    You could use the normal “send_headers” action to conditionally send your header. Or use the slightly earlier “wp_headers” filter to conditionally add your header to the WP list, letting WP do the actual sending. Whether this is late enough or not for any particular plugin is anyone’s guess. You could at least test with WooCommerce to see if it works at least for WC pages.

    Or you could use the latest possible action prior to output, which is “template_include”. If DONOTCACHEPAGE is not yet defined, there is no help for it anyway as it relates to sending headers. Template_include is actually a filter, but you would be using it for its timing, not to alter what template will be loaded. Your callback would return the passed template unchanged. The trouble here is there is some possibility that output could occur prior to this. It shouldn’t, but it could.

    Does whatever you are trying to achieve have to be done through a header? Is there any way to achieve it through JavaScript? If the script to do so is conditionally added to the footer, DONOTCACHEPAGE would surely be defined by then.

    Thread Starter Rodrigo Gomes

    (@rodrigogomes-1)

    Hello @bcworkz,

    Thank you for your time in such a detailed response.

    template_include actually work!

    function my_function($template) {
            if(defined('DONOTCACHEPAGE')) {
                    header("MyCustomHeader: 12345");
            }
            return $template;
        }
    add_filter( 'template_include', 'my_function' );

    I’m going to do a few more tests, but it works perfectly so far.

    What I’m going to do is much more complex.
    I will use this Header to communicate with the Varnish Cache (proxy server) and set when it should not cache some content.

    As Varnish Cache will only know that content can only be cached after actually accessing it, the connection will repeat itself.
    To save resources from the server, I set to stop processing the page once it realizes that it is the access that will be repeated and after sending the header.

    if(defined('DONOTCACHEPAGE') || defined('DOING_CRON') || is_admin()) {
    	if (!headers_sent() && $_SERVER['HTTP_X_SERVER'] == "blizhost-varnish") {
    		header("BypassVarnishCache: TRUE");
    		if(!$_SERVER['HTTP_X_BYPASSVARNISH']){ exit; }
    	}
    }

    Because of Varnish’s limitations as a proxy, this was the best way I found to do this.
    If it gets confusing, I can try to explain it another way.

    Note: The connection needs to be repeated because the cookies sent on the first access will be sent to the second. Limitation of Varnish Cache with cookies.

    Moderator bcworkz

    (@bcworkz)

    Ah, sure, I understand. Clever idea ?? As a generic solution it has problems, but it’s great when it works. My concern is other plugins will use a different constant besides DONOTCACHEPAGE. You’d probably need to add new values for every situation IF constants are even used at all. Or they may not use constants, but some other mechanism. That would still be OK as long as it’s globally available when “template_include” fires. It’s conceivable a plugin simply outputs a no-cache meta tag and does not decide to do so until it’s too late to send headers — that Catch-22.

    Or they may output Cache-Control or Pragma headers only. You can check for these in the “wp_headers” filter, but if they are set by .htaccess rules, all PHP can do is read the file, searching for the appropriate Header set rules. Or does Varnish check for these headers anyway?

    If your main goal is to work with WooCommerce and other plugins are merely a nice bonus, you’re all set with DONOTCACHEPAGE.

    Thread Starter Rodrigo Gomes

    (@rodrigogomes-1)

    Hello @bcworkz,
    Thank you! ?? You’re right. Is not the ideal solution.

    Due to the cache plugins (WP Super Cache, W3 Total Cache, etc.), some plugins (the most important ones at least) understand that they should use DONOTCACHEPAGE for more compatibility. This gave me this idea to improve the compatibility of my Varnish with some plugins.

    But, it’s just an add-on, in my VCL I have already configured that Varnish should skip some pages. For example:

    	if (req.url ~ "(?i)/wp-admin|/admin/|/adm/|/login/|/wp-(login|signup|activate|mail|cron)\.php|preview\=true|/xmlrpc\.php|/bb-admin|control\.php|bb-login\.php|bb-reset-password\.php|register\.php") {
    		if (req.url !~ "(?i)^[^?]*\.(bmp|css|gif|ico|jpeg|jpg|js|png|svg|svgz)(\?.*)?$") {
    			set req.http.X-BypassVarnish = "allready";
    			set req.http.X-BackendUncacheable = "admin-page";
    			return(pass);
    		}
    	}
    	if (req.url ~ "(?i)/(cart/|my-account/|checkout/|addons/|logout/|lost-password/)|(\?add-to-cart=|\?wc-api=)") { 
    		set req.http.X-BypassVarnish = "allready";
    		set req.http.X-BackendUncacheable = "admin-page";
    		return (pass); 
    	}
    	if (req.http.Cookie) {
    		if (req.http.Cookie ~ "(?i)wordpress_logged_in_|comment_|wp-postpass_") {
    			set req.http.X-BackendUncacheable = "cookie-session";
    			set req.http.X-BypassVarnish = "allready";
    			return(pass);
    		}
    	}

    This is only for some cases where some people rename the standard WooCommerce page or the WordPress administrative page.

    It’s conceivable a plugin simply outputs a no-cache meta tag and does not decide to do so until it’s too late to send headers — that Catch-22.

    And you’re right again, If the Header is not sent before, it will not have another chance, but the person can clear the cache manually when this happens or ask me to bypass the cache in VLC, it is not a very serious problem.

    Or they may output Cache-Control or Pragma headers only. You can check for these in the “wp_headers” filter, but if they are set by .htaccess rules, all PHP can do is read the file, searching for the appropriate Header set rules. Or does Varnish check for these headers anyway?

    By default, Varnish Cache work that way, analyzing the Cache-Control headers.
    But to get a higher cache hit rate on Varnish, I’d rather customize my cache completely.
    So I get a great saving of resources, minimally compromising the functionality of WordPress.

    The final code looks like this:

    	function donotcache() {
    		if(defined('DONOTCACHEPAGE') || defined('DOING_CRON') || is_admin() || $GLOBALS['pagenow'] === 'wp-login.php') {
    			// Do not set header if header has already been sent
    			if (!headers_sent() && $_SERVER['HTTP_X_SERVER'] == "blizhost-varnish") {
    				header("BypassVarnishCache: TRUE");if(!$_SERVER['HTTP_X_BYPASSVARNISH']){exit;}
    			}
    		}
    	}
    	function donotcache_filter($template) {
    		donotcache();
    		return $template;
    	}
    	add_filter( 'init', donotcache );
    	add_filter( 'template_include', donotcache_filter );

    I really appreciate your help and opinion! ??

    Moderator bcworkz

    (@bcworkz)

    Thanks for the explanation! I imagine there is no ideal solution. It sounds like you have done as much as is possible to improve things. Some things just require manual “tuning”. Cheers.

    Thread Starter Rodrigo Gomes

    (@rodrigogomes-1)

    I’ve been working on it for over a year, with so many tests and studies. And the best way to do this is to make Varnish ignore the Cache-Control headers and create my own rules.

    All this to offer an easy solution for some people to cache their websites using Varnish Cache + WordPress, without the need for advanced knowledge or programming VCL.

    I love WordPress, I’m super fan of you guys and I’m creating with the best of my abilities a solution for some people who have a lot of access and little money to pay very expensive servers. ??

    Thanks for the excellent work you guys have done and for the wonderful support.

    Moderator bcworkz

    (@bcworkz)

    That’s quite an endorsement! On behalf of the WordPress community, you are most welcome and thank you for your support by promoting WordPress ??

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘Creating a custom header if DONOTCACHEPAGE is True’ is closed to new replies.