7

I have a complex install of Joomla 3.4.4 with jSeblod and many other extensions. We are planning to use Varnish4 in front of the web server and because of that need to turn on proper Cache-Control headers.

But I cannot seem to turn on caching. Here is what I have tried:

1)

configuration.php

public $caching = '1';
public $cache_handler = 'file';
public $cachetime = '30';

2) Administrator->Extensions->Plugins->Page Caching is enabled

In the plugin, Browser Caching is enabled

3) With a debugger, the debugger stops in any plugin I try, but not in the cache plugin

4) Under Administrator->Extensions->Manage I can find the Cache plugin installed

5) Under Administrator->Extensions->Discover, it is now found.

6) On my own live website I get the same results, however the Cache header is "Cache-Control: no-cache" instead of "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"

7) I have built a plugin which turns the cache on just before the Response is sent:

class plgSystemGtnocachies extends JPlugin
{
    function plgSystemGtnocachies( &$subject, $config )
    {
        parent::__construct( $subject, $config );
    }

    function __destruct()
    {
        if (!headers_sent())
        {
            $this->setCacheHeaders();
        }
    }

    public function onAfterRender()
    {
        JApplicationWeb::allowCache( true );
        $this->setCacheHeaders();
    }

    private function setCacheHeaders()
    {
        JApplicationWeb::setHeader( 'Cache-Control', 'public, max-age=10800', true );
        JApplicationWeb::setHeader( 'Vary', 'Cookie', true );
        JApplicationWeb::setHeader( 'Pragma', '', true );
    }
}

What I find however is that when the function call returns to

class JEventDispatcher extends JObject
{
    public function trigger($event, $args = array())
    {
        $result = array();

        /*
         * If no arguments were passed, we still need to pass an empty array to
         * the call_user_func_array function.
         */
        $args = (array) $args;

        $event = strtolower($event);

        // Check if any plugins are attached to the event.
        if (!isset($this->_methods[$event]) || empty($this->_methods[$event]))
        {
            // No Plugins Associated To Event!
            return $result;
        }

        // Loop through all plugins having a method matching our event
        foreach ($this->_methods[$event] as $key)
        {
            // Check if the plugin is present.
            if (!isset($this->_observers[$key]))
            {
                continue;
            }

            // Fire the event for an object based observer.
            if (is_object($this->_observers[$key]))
            {
                $args['event'] = $event;
                $value = $this->_observers[$key]->update($args);
------->
            }
            // Fire the event for a function based observer.
            elseif (is_array($this->_observers[$key]))
            {
                $value = call_user_func_array($this->_observers[$key]['handler'], $args);
            }

            if (isset($value))
            {
                $result[] = $value;
            }
        }

        return $result;
    }

JApplicationWeb::getInstance->response->cacheable is false again.

Any idea what is going on here?

2

I have solved this problem by using an output buffer wrapper around the whole Joomla installation.

Some background:

In this website we are using jSeblod, RSForm, JCHOptimize, Nonumber Joomla CDN and AceSEF. There are also a lot of plugins. From a code search I determined that most extensions seem to emit their own cache headers, making it impossible to get this under control from a Joomla perspective.

I was hoping I could just erase all the headers in OnAfterRender, but as you can see in the question, this doesn't work at all.

We needed to Cache all pages with Varnish, and ideally have the cache turned off in Joomla, so that direct testing is easier. We also needed to have Caching turned off for - Pages containing RSForm - we always embed this into content - The homepage, because it has GeoIP bases country redirection - When someone is logged in, both in Administrator and frontend, as we are using this to stage content.

The solution involves setting a prepend file in the VHost, a system plugin to identify pages to not cache and the contents of the prepend file.

Our cache time is hardcoded at 1800 seconds:

VHost:

 php_value auto_prepend_file /path/to/my/webfolder/prepend.php

Custom system plugin:

class plgSystemCacheOn extends JPlugin
{

    function plgSystemCacheOn(&$subject, $config)
    {
            parent::__construct($subject, $config);
    }

    private function getCleanRequestUri()
    {
        list($requestUri, $query) = explode('?', $_SERVER["REQUEST_URI"]);
        return $requestUri;
    }


    private function isHomepage()
    {
        return $this->getCleanRequestUri() === '/' && 
        (!isset($_GET['option']) || !$_GET['option']);
    }


    function onAfterRender()
    {
        $app = JFactory::getApplication();
        $jinput = $app->input;

        if ($app->getName() != 'site') {
            return true;
        }

        $buffer = JResponse::getBody();

        if ($this->isHomepage()){
            header('GTCacheOn: KeepOn');
        }
        if (strstr($buffer, '{rsform')){
            header('GTCacheOn: KeepOn');
        }

        if (JFactory::getUser()->id > 0){
            header('GTUser: Keep');
        }

        return true;
    }
}

prepend.php

function dontCache($headers){

    if ($headers && is_array($headers)){
        foreach($headers as $header){
            list ($headerName, $headerData) = explode( ':', $header);
            if ($headerName === 'GTCacheOn' && trim($headerData) == 'KeepOn') return true;
            if ($headerName === 'GTUser' && trim($headerData) == 'Keep') return true;
        }
    }
    return false;
}    

function only_greentree_headers_allowed( $buffer, $phase )
{
    header_remove( 'Pragma' );
    require (dirname( __FILE__ ). "/libraries/greentree/Helper.php");
    if (in_array($_SERVER['SCRIPT_NAME'], array(
        '/index.php',
        '/media/plg_jchoptimize/assets2/jscss.php'
    ))) {
        header_remove('Expires');
        header_remove('Set-Cookie');
        if (!GreentreeHelper::dontCache(headers_list())) {
            header('Cache-Control: public, max-age=1800', true);
            header('Vary: Cookie', true);
        } else {
            header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0', true);
        }
    }else{
        header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0', true);
    }

    return $buffer;
}

ob_start( 'only_my_headers_allowed' );
0

There is a lot of information on this website http://massivescale.net/varnish-joomla.html

They say that a plugin cannot solve this issue due to the way the Joomla! core is written. They do however offer some sort of a hack on their site for £70. I would be weary of using it on a site with a lot of add-ons.

SEBLOD is based on the core content component so may be alright. I would check other plugins individually, and if you have problems turn them off one at a time.

  • Hi, many thanks. I have come to the same conclusion as them and am happy to publish my solution, which is via a prepend file. I'm looking for an authorative answer, for example someone saying how the Joomla code should be modified for it to work. – jdog Oct 26 '15 at 23:22
  • That page you linked... Their solution is for 1.5 with 1.6/1.7/2.5 not supported. Probably not worth paying for. Unless it is just the page that is out of date, of course. – Ville Niemi Oct 27 '15 at 3:51
  • @Ville It also works with Joomla 3.x if you look closely. Only 1.6, 1.7. 2.6 are not supported, which I think is a smart decision. I had myself 2 clients which have run 1.5 until recently. These sites were built when Joomla 2.5 was just out, but Virtuemart not available. It turned out uneconomical to upgrade them, if you take the position that Virtuemart is not a good way to go anymore. Rebuild cheaper. – jdog Oct 27 '15 at 19:33
  • @jdog So it was just page not up-to-date? And yeah I remember doing 1.5 to 2.5 migrations. On a complex site you are essentially trying to sell a new site. – Ville Niemi Oct 28 '15 at 3:55
  • @Ville FAQ What is included in $70 package? Patches for Joomla 1.5, 2.5.4+, 3.0, 3.1, 3.2, 3.3, 3.4 VCL files for Varnish 2.1+, 3.x and 4.x, a plugin purging articles from cache (for Joomla 2.5 or higher), and 17 pages of documentation in PDF, ODT and TXT. We also add 30 minutes of support, including installation. You see every version of Joomla excl. 1.6, 1.7, 2.5.1/2/3 is listed – jdog Oct 28 '15 at 19:32

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.