10 ways to speed up the Opencart 3 and 4 – website speed optimization

In this Opencart tutorial on website speed optimization, we are showing you 10 ways to speed up Opencart 3 and 4 which you can do from the free Opencart module and tips provided below. This helps to optimize website speed in Opencart and increase opencart page load speed.

Choose a better hosting provider and better cache module

Just choose a better hosting provider for Opencart, better is always expensive so choose as per your budget. Choose a good cache module for Opencart. If you are using the shared hosting then ask them which cache are they providing and use the cache module as per it, our is using LSCache so we use the LSCache module.

Defer all the extra CSS and JS at the footer.

In the module, we just defer all the JavaScript with ‘defer=”defer”‘, with this, the script will not run until after the page has loaded, better to use only for the external scripts.

<script defer='defer' src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="catalog/view/javascript/common.js" type="text/javascript"></script>

For jQuery, we load at first because it is a building block so we load at the header and without defer. The following is the Ocmod XML which makes those changes. This is done with above downloaded module and works only for the Opencart 3, for opencart 4 we will provide it soon.

<file path="catalog/controller/common/header.php">
    <operation>
      <search>
        <![CDATA[
		      $data['links'] = $this->document->getLinks();
        ]]>
      </search>
      <add position="replace">
        <![CDATA[
        $data['links'] ="";
          //$data['links'] = $this->document->getLinks();
        ]]>
      </add>
    </operation>
    <operation>
      <search><![CDATA[
		$data['styles'] = $this->document->getStyles();
]]>      </search>
      <add position="replace"><![CDATA[
      $data['styles'] ="";
  //$data['styles'] = $this->document->getStyles();
]]>      </add>
    </operation>
  </file>
  <file path="catalog/controller/common/footer.php">
    <operation>
      <search><![CDATA[
		$data['scripts'] = $this->document->getScripts('footer');
]]>      </search>
      <add position="after"><![CDATA[
    $data['links'] = $this->document->getLinks();
    $data['styles'] = $this->document->getStyles();
    //$data['scripts'] = $this->document->getScripts();
]]>      </add>
    </operation>
  </file>
  <file path="catalog/view/theme/*/template/common/header.twig">
    <operation>
      <search><![CDATA[
<script src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
]]>      </search>
      <add position="replace" offset="6"><![CDATA[
<!--<script src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<link href="catalog/view/javascript/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,400i,300,700" rel="stylesheet" type="text/css" />
<link href="catalog/view/theme/default/stylesheet/stylesheet.css" rel="stylesheet">-->
]]>      </add>
    </operation>
    <operation>
      <search><![CDATA[
<script src="catalog/view/javascript/common.js" type="text/javascript"></script>
]]>      </search>
      <add position="replace" offset="3"><![CDATA[
<!--<script src="catalog/view/javascript/common.js" type="text/javascript"></script>-->
]]>      </add>
    </operation>
  </file>
  <file path="catalog/view/theme/*/template/common/footer.twig">
    <operation>
      <search>
        <![CDATA[{% for script in scripts %}]]>
      </search>
      <add position="before"><![CDATA[
<link href="catalog/view/javascript/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<link href="catalog/view/javascript/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,400i,300,700" rel="stylesheet" type="text/css" />
<link href="catalog/view/theme/default/stylesheet/stylesheet.css" rel="stylesheet">
    
{% for style in styles %} 
<link href="{{ style.href }}" type="text/css" rel="{{ style.rel }}" media="{{ style.media }}" />
{% endfor %}
{% for link in links %}
<link href="{{ link.href }}" rel="{{ link.rel }}" />
{% endfor %}
<script defer="defer" src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="catalog/view/javascript/common.js" type="text/javascript"></script>
]]>      </add>
    </operation>
  </file>

Use the image sizes properly

One idea to size the image is to use the ratio in all image settings. The best image ratio is 16:9. So, create images of size 1200px width and 675 px height and in all settings use a 16:9 ratio. Go to Admin >> Extensions >> Extensions >> Choose Theme as extension type >> Then edit your active theme >> Then enter the sizes for Images in the ratio of 16:9, like we are using it as:

Opencart image sizes width and height

Use the proper extension for the image:

JPEGs are for photographs and realistic images. PNGs are for line art, text-heavy images, and images with few colors. See the difference

PNG vs JPEG load time

You can easily convert the PNG to jpeg or jpeg to PNG online as well as on Photoshop. Like, we use https://www.photopea.com/ online tool to convert them which is easy and fast.

Convert Jpeg to Png

Optimize the image properly

Use the ImageOptim for properly optimizing the image. It optimizes as per the page speed insight. You can download the ImageOptim here. Right-click the image and open with ImageOptim and it will optimize and replace the image with an optimized one.

image optimizer opencart

Lazy loading of images:

With just adding loading=”lazy” in the image tag it will lazy load the images.

 <img loading="lazy" src="catalog/language/en-gb/en-gb.png" alt="English" title="English">

Created one module for lazy loading of the image:

<file path="catalog/view/theme/*/*/*/*.twig|catalog/view/theme/*/template/*/*/*.twig">
    <operation>
      <search>
        <![CDATA[<img ]]>
      </search>
      <add position="replace">
        <![CDATA[<img loading="lazy"   ]]>
      </add>
    </operation>
  </file>

GZIP for more efficient transfer to requesting clients. The compression level must be between 0 – 9.

Gzip Compression is an effective way to reduce the size of files. To enable the text compression in Opencart, go to Admin >> System >> Settings >> Server tab >> Add the “Output Compression Level”. The value should be 0-9, what we find out is most of the time it works above 5 but hit and trial is the only option that we see. With these, it minimizes the byte size of network responses and fewer bytes means the page loads fast.

Speed up the repeat visit by serving static assets with an efficient cache policy 

You can serve static assets with an efficient cache policy by adding the following code in the .htaccess file, these are just our ideas, you can make changes as per your requirement and this is code to add on the .htaccess for Apache server, if you are using nginx then you can configure similarly

# Set up 1 week caching on javascript and CSS
<FilesMatch “\.(js|css)$”>
ExpiresDefault A604800
Header append Cache-Control “proxy-revalidate”
SetOutputFilter DEFLATE
</FilesMatch>
# LBROWSERCSTART Browser Caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/gif “access 1 year”
ExpiresByType image/jpg “access 1 year”
ExpiresByType image/jpeg “access 1 year”
ExpiresByType image/png “access 1 year”
ExpiresByType image/x-icon “access 1 year”
ExpiresByType text/css “access 1 month”
ExpiresByType text/javascript “access 1 month”
ExpiresByType text/html “access 1 month”
ExpiresByType application/javascript “access 1 month”
ExpiresByType application/x-javascript “access 1 month”
ExpiresByType application/xhtml-xml “access 1 month”
ExpiresByType application/pdf “access 1 month”
ExpiresByType application/x-shockwave-flash “access 1 month”
ExpiresDefault “access 1 month”
</IfModule>
# END Caching LBROWSERCEND

Compress and minify the output

<file path="system/library/response.php">
        <operation>
            <search position="replace"><![CDATA[
    public function output() {
            ]]></search>
            <add><![CDATA[
    public function output() {
        if ($this->output) {
            $this->output = preg_replace("/(\n)+/", "\n", $this->output);
            $this->output = preg_replace("/\r\n+/", "\n", $this->output);
            $this->output = preg_replace("/\n(\t)+/", "\n", $this->output);
            $this->output = preg_replace("/\n(\ )+/", "\n", $this->output);
            $this->output = preg_replace("/\>(\n)+</", '><', $this->output);
            $this->output = preg_replace("/\>\r\n</", '><', $this->output);
        }
            ]]></add>
        </operation>
    </file>

Minify your HTML, CSS, and JS

<file path="system/library/template.php">
    <operation>
      <search><![CDATA[return $this->adaptor->render($template, $cache);]]></search>
      <add position="replace"><![CDATA[
    if (strpos($template, 'template/') !== false) {
        return $this->minify($this->adaptor->render($template, $cache));
    } else {
        return $this->adaptor->render($template, $cache);
    }
            ]]>      </add>
    </operation>
    <operation>
      <search><![CDATA[private $adaptor;]]></search>
      <add position="after"><![CDATA[
    /**
	 * @param	string	$body
	 * @return	string
 	*/
	public function minify($body) {
        $search = array(
            '/\>[^\S ]+/s',     // strip whitespaces after tags, except space
            '/[^\S ]+\</s',     // strip whitespaces before tags, except space
            '/(\s)+/s',         // shorten multiple whitespace sequences
        );
        $replace = array(
            '>',
            '<',
            '\\1',
            ''
        );
        $body = preg_replace($search, $replace, $body);
        return $body;
    }
            ]]>      </add>
    </operation>
  </file>

Index the database table

First backup your database.
Download the turbo.php, upload it where Opencart is installed, run YOURSITEURL/turbo.php and click the “Add Database Indexes” button, this will index all the database tables as per column name.

https://github.com/lilalaunesau/opencart-turbo/blob/master/turbo.php

Or you can run the following SQL directly in your database:

ALTER TABLE `oc_category` ADD INDEX ( `parent_id` ) ;
ALTER TABLE `oc_category` ADD INDEX ( `top` ) ;
ALTER TABLE `oc_category` ADD INDEX ( `sort_order` ) ;
ALTER TABLE `oc_category` ADD INDEX ( `status` ) ;
ALTER TABLE `oc_category_description` ADD INDEX ( `language_id` );
ALTER TABLE `oc_category_to_store` ADD INDEX ( `store_id` );
ALTER TABLE `oc_category_path` ADD INDEX ( `path_id` );
ALTER TABLE `oc_product` ADD INDEX ( `model` ) ;
ALTER TABLE `oc_product` ADD INDEX ( `sku` ) ;
ALTER TABLE `oc_product` ADD INDEX ( `upc` ) ;
ALTER TABLE `oc_product` ADD INDEX ( `manufacturer_id` ) ;
ALTER TABLE `oc_product` ADD INDEX ( `sort_order` ) ;
ALTER TABLE `oc_product` ADD INDEX ( `status` ) ;
ALTER TABLE `oc_product_option` ADD INDEX ( `option_id` ) ;
ALTER TABLE `oc_product_option_value` ADD INDEX ( `product_option_id` ) ;
ALTER TABLE `oc_product_option_value` ADD INDEX ( `product_id` ) ;
ALTER TABLE `oc_product_option_value` ADD INDEX ( `option_id` ) ;
ALTER TABLE `oc_product_option_value` ADD INDEX ( `option_value_id` ) ;
ALTER TABLE `oc_product_to_category` ADD INDEX ( `category_id` );
ALTER TABLE `oc_product_attribute` ADD INDEX ( `attribute_id` );
ALTER TABLE `oc_product_attribute` ADD INDEX ( `language_id` );
ALTER TABLE `oc_product_description` ADD INDEX ( `language_id` );
ALTER TABLE `oc_product_to_store` ADD INDEX ( `store_id` );
ALTER TABLE `oc_option` ADD INDEX ( `sort_order` ) ;
ALTER TABLE `oc_option_description` ADD INDEX ( `name` ) ;
ALTER TABLE `oc_option_value` ADD INDEX ( `option_id` ) ;
ALTER TABLE `oc_option_value_description` ADD INDEX ( `option_id` ) ;
ALTER TABLE `oc_url_alias` ADD INDEX ( `query` ) ;
ALTER TABLE `oc_url_alias` ADD INDEX ( `keyword` ) ;
ALTER TABLE `oc_url_alias` ADD INDEX ( `url_alias_id` );

Developer or Designer tasks: Ensure text remains visible during Webfont load

Follow the idea provided at https://developers.google.com/web/updates/2016/02/font-display. Just for your information, we tried that and in our case, we used font-display: swap, and only works. Something like below:

@font-face {
font-family: ‘Arvo’;
font-display: swap;
src: local(‘Arvo’), url(https://fonts.gstatic.com/s/arvo/v9/rC7kKhY-eUDY-ucISTIf5PesZW2xOQ-xsNqO47m55DA.woff2) format(‘woff2’);
}

Look for Critical CSS: Defer unused CSS, remove all unused CSS on a page, and try to target CSS for each page.

https://developers.google.com/web/tools/lighthouse/audits/unused-css

In this way, you can perform the Opencart website speed optimization, please let us know if you have any other tips and tricks. You can visit Opencart SEO to read around 25 best practices for Opencart SEO. Let us know if you have questions or concerns so that we can help you out. Till then please subscribe to our YouTube Channel for Opencart video tutorials. You can also find us on Twitter and Facebook.

The whole code XML is below:

<?xml version="1.0" encoding="utf-8"?>
<modification>
  <name>10 ways to speed up the Opencart site</name>
  <code>webocreationpagespeed100</code>
  <version>1.0.0</version>
  <author>Rupak Nepali</author>
  <link>https://webocreation.com</link>
  <file path="catalog/view/theme/*/*/*/*.twig|catalog/view/theme/*/template/*/*/*.twig">
    <operation>
      <search>
        <![CDATA[<img ]]>
      </search>
      <add position="replace">
        <![CDATA[<img loading="lazy"  ]]>
      </add>
    </operation>
  </file>
  <file path="system/library/response.php">
    <operation>
      <search position="replace"><![CDATA[
    public function output() {
            ]]>      </search>
      <add><![CDATA[
    public function output() {
        /*source compressor*/
        if ($this->output) {
            //$this->output = preg_replace('/<!--(.|\n)*?-->/', " ", $this->output);
            $this->output = preg_replace("/(\n)+/", "\n", $this->output);
            $this->output = preg_replace("/\r\n+/", "\n", $this->output);
            $this->output = preg_replace("/\n(\t)+/", "\n", $this->output);
            $this->output = preg_replace("/\n(\ )+/", "\n", $this->output);
            $this->output = preg_replace("/\>(\n)+</", '><', $this->output);
            $this->output = preg_replace("/\>\r\n</", '><', $this->output);
        }
            ]]>      </add>
    </operation>
  </file>
  <file path="catalog/controller/common/header.php">
    <operation>
      <search>
        <![CDATA[
		      $data['links'] = $this->document->getLinks();
        ]]>
      </search>
      <add position="replace">
        <![CDATA[
        $data['links'] ="";
          //$data['links'] = $this->document->getLinks();
        ]]>
      </add>
    </operation>
    <operation>
      <search><![CDATA[
		$data['styles'] = $this->document->getStyles();
]]>      </search>
      <add position="replace"><![CDATA[
      $data['styles'] ="";
  //$data['styles'] = $this->document->getStyles();
]]>      </add>
    </operation>
  </file>
  <file path="catalog/controller/common/footer.php">
    <operation>
      <search><![CDATA[
		$data['scripts'] = $this->document->getScripts('footer');
]]>      </search>
      <add position="after"><![CDATA[
    $data['links'] = $this->document->getLinks();
    $data['styles'] = $this->document->getStyles();
    //$data['scripts'] = $this->document->getScripts();
]]>      </add>
    </operation>
  </file>
  <file path="catalog/view/theme/*/template/common/header.twig">
    <operation>
      <search><![CDATA[
<script src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
]]>      </search>
      <add position="replace" offset="6"><![CDATA[
<!--<script src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<link href="catalog/view/javascript/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,400i,300,700" rel="stylesheet" type="text/css" />
<link href="catalog/view/theme/default/stylesheet/stylesheet.css" rel="stylesheet">-->
]]>      </add>
    </operation>
    <operation>
      <search><![CDATA[
<script src="catalog/view/javascript/common.js" type="text/javascript"></script>
]]>      </search>
      <add position="replace" offset="3"><![CDATA[
<!--<script src="catalog/view/javascript/common.js" type="text/javascript"></script>-->
]]>      </add>
    </operation>
  </file>
  <file path="catalog/view/theme/*/template/common/footer.twig">
    <operation>
      <search>
        <![CDATA[{% for script in scripts %}]]>
      </search>
      <add position="before"><![CDATA[
<link href="catalog/view/javascript/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<link href="catalog/view/javascript/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,400i,300,700" rel="stylesheet" type="text/css" />
<link href="catalog/view/theme/default/stylesheet/stylesheet.css" rel="stylesheet">
{% for style in styles %} 
<link href="{{ style.href }}" type="text/css" rel="{{ style.rel }}" media="{{ style.media }}" />
{% endfor %}
{% for link in links %}
<link href="{{ link.href }}" rel="{{ link.rel }}" />
{% endfor %}
<script defer="defer" src="catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="catalog/view/javascript/common.js" type="text/javascript"></script>
]]>      </add>
    </operation>
  </file>
  <file path="system/library/template.php">
    <operation>
      <search><![CDATA[return $this->adaptor->render($template, $cache);]]></search>
      <add position="replace"><![CDATA[
    if (strpos($template, 'template/') !== false) {
        return $this->minify($this->adaptor->render($template, $cache));
    } else {
        return $this->adaptor->render($template, $cache);
    }
            ]]>      </add>
    </operation>
    <operation>
      <search><![CDATA[private $adaptor;]]></search>
      <add position="after"><![CDATA[
    /**
	 * @param	string	$body
	 * @return	string
 	*/
	public function minify($body) {
        $search = array(
            '/\>[^\S ]+/s',     // strip whitespaces after tags, except space
            '/[^\S ]+\</s',     // strip whitespaces before tags, except space
            '/(\s)+/s',         // shorten multiple whitespace sequences
        );
        $replace = array(
            '>',
            '<',
            '\\1',
            ''
        );
        $body = preg_replace($search, $replace, $body);
        return $body;
    }
            ]]>      </add>
    </operation>
  </file>
</modification>
Previous articleAdministrator Theme editor in Opencart 3.0.2.0 default theme
Next articleHow does Affiliate work in Opencart 4?
Author of three Opencart book. The recent Opencart 4 book is at https://amzn.to/4dOlbOR

23 COMMENTS

  1. OK: The article is not nearly as useful as it appears.

    1. The turbo.php script does not fully work, (Fails on several tables) not updated since 2013. — So I am not sure if I have caused some issue with my database.

    2. Enabling the caching functions is 10x harder than adding the .htaccess files. The modules are not enabled by default and there are 3 modules and 2 configurations plus the vhost file to modify.

    3. The instructions on minify are not really useful code snippets because I have no idea how to implement them.

    • Hi Colin,
      1. The turbo.php is not specific to any version of the Opencart it is general indexing by finding the database tables and creating the indexing. Check the code like:
      $sql = “SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE ‘”.DB_DATABASE.”‘”;
      This finds all the tables in the database and loop through the tables so even it is old it should have worked. I am not sure what is failed, please let us know and we may investigate.

      2. Yes if you are controlling your server then yes need to perform those, but most of the host comes with those so you can just add in the .htaccess and it works.

      3. For minify, you download the module above and install it. We tested for default opencart v 3 so if you are using customized theme then it can be different.

      Thanks for the feedback

  2. OK: The article is not nearly as useful as it appears.

    1. The turbo.php script does not fully work, (Fails on several tables) not updated since 2013. — So I am not sure if I have caused some issue with my database.

    2. Enabling the caching functions is 10x harder than adding the .htaccess files. The modules are not enabled by default and there are 3 modules and 2 configurations plus the vhost file to modify.

    3. The instructions on minify are not really useful code snippets because I have no idea how to implement them.

  3. Hi

    I just installed this however this has now affected the layout of my website and would like to uninstall it. Kindly assist with uninstalling this

  4. This is perfect but my theme is Journal3. Please help me, how to speedup Journal3. In this article, some modifications work perfectly for me but, help me how to best speed for Journal3.

    • For Journal theme, go to admin >> Journal >> System >> Settings >> Performance, you can find multiple options for performance and optimizations

  5. This is perfect but my theme is Journal3. Please help me, how to speedup Journal3. In this article, some modifications work perfectly for me but, help me how to best speed for Journal3.

  6. It was awesome for 30 seconds it was like a rocket. But after 30sec-1min it has turned to old very low speed. Why happened this?

  7. Can You please explain how “Index the database table” works?
    Can it affect the modules which is not in the default installation?
    What happens if I lets say change the sizes of the product? Do I need to index again or does it index automatically?

  8. Hi Rupak.
    First, accept my apologizes, mi english is too bad but, i make an effort in this language.

    My problem is with OpenCart 3, version 3.0.3.6.
    I can install your module with no errors in this. but when i apply modifications and clear it (and cache too) the page worsk fast but two buttons (may be another) where disabled with no action:
    – Login / register.
    – Add To Cart

    I am not an expert but i had to uninstall the extension to make those buttons works again.

    Can you guide me a bit about it?

    Thank you.

LEAVE A REPLY

Please enter your comment!
Please enter your name here