Improving perceived load time

Dylan Bridgman
Aug 5, 2015 · 5 min read

There are numerous ways to improve overall page load time.

In this article I will concentrate on perceived load time. That is the time from when a page starts loading until the user is able to proceed. There have been other articles on this topic, some of which go into far more detail in some areas. My intention is to give a summary on the latest information and what you should be doing in PHP. I will link to additional information where applicable.

To improve perceived load time you should use output buffering, multiple connections and correctly structured pages. All of these fall under the broader topic of progressive HTML rendering.

Many of these changes may contradict those recommended for overall page load time. If used sparingly however, and in the right places, they will dramatically improve perceived load time for the user.

Output buffering

There are a few buffers involved when delivering the output of your PHP code to the browser. The data may be buffered in PHP, the web server, and the browser.

Output buffering should be enabled in PHP to control when data is sent. This is done either by the output_buffering directive in the ini file or by calling ob_start() before any output. Both the ob_flush() and flush() functions must then be used at selected points in the output.

Then the following items should be considered to minimise the time taken for data leaving PHP to be rendered by the browser.

Disable compression

Compression should be disabled to take advantage of output buffering. When enabled the data may not be sent until the current compression buffer is full, even if the PHP output buffer is flushed.

In order to keep the benefit of compression for other pages and resources, this should only be done for pages where control of output buffering is needed.

If you are using Apache this may be controlled using the apache_setenv() function or using htaccess settings. For Nginx use locations with the gzip setting.

Remember to also disable PHP output compression.

Character encoding

Browsers will often wait until the character encoding of a page is known before rendering. If it is not explicitly set then 512 bytes or more data will be buffered and used to guess the setting.

Setting the character encoding using a header() call, ini file directive, or meta tag will prevent this. For PHP 5.6 and above this will default to UTF-8 and no changes may be necessary.

Browser buffering

The PHP manual has the following to say about the flush() function:

Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.

After disabling compression and setting the character encoding, both Internet Explorer and Safari are affected by this delay. Firefox and Chrome both start rendering immediately. Internet Explorer still requires 256 bytes of output to start rendering.

Safari is a bit more complicated. Older versions require 1024 bytes and newer versions 512 bytes. In testing Safari 8 I found that this does not work on the first load of a page but does work on subsequent refreshes (a bug?).

The ob_get_length() function may be used to calculate the correct amount of extra whitespace needed.

An important observation is that this only applies to the first output sent to the browser. Subsequent flushes of the buffer are not affected because the browser enters a progressive HTML rendering mode.

Multiple connections

Articles on optimising overall page load time emphasise the minimising of HTTP requests. This is good advice because more connections mean more overhead. However, sometimes using a few extra connections may help both the perceived load time as well as the overall load time.

There are limits on the number of concurrent connections a browser will make. The average browser allows for six connections per hostname and most will allow at least ten connections in total. One connection will be used by the page loading. That leaves five connections to be used for loading other resources from the same hostname or nine connections if other hostnames are used.

These resources could be CSS, JavaScript, images or templates. You should carefully analyse the resources you load, prioritise them and change the code to load them in this order.

As a rule of thumb this means the resources needed by the items at the top of the page. Remember that interacting with those items may need further resources to be loaded and this should be taken into account.

Try to use all connections, if this many resources are needed, with the first section of the page sent to the browser. The earlier additional connections are initialised the better.

Keep in mind that any JavaScript you include will be run before the HTML following it will start rendering, so keep it as short as possible. Use progressive enhancement if possible.

If you have any connections left you may want to use the async attribute to preload JavaScript that is only needed at a later point.

Page structure

As soon as usable content appears in the browser the user may begin interacting with the page. You do not want to create a situation where content appears but is not usable.

The structure of the page should be organised so that as sections of the page are rendered by the browser, they are ready to be used. That means if you want the header to appear first, make sure the logo image and header CSS are also loaded.

Avoid sending portions of a section by using output buffering and only flushing the buffer when a section is complete.

Page and user types

Not all users are created equal. When users hit your landing page they may be using the menu to go to another page, browsing the content or looking for a link in your sitemap.

The same logic applies for the pages themselves. A user hitting a blog page is most likely there to read the content whereas users on the front page are more likely to be clicking a link, signing up or logging in.

Any changes made to page structure should consider both the type of page being loaded and the different users who will visit.

Example

Here is an easy to follow example combining all the techniques mentioned above.

If you are using something other than Apache you may need to make additional changes to disable compression.

As you most likely realise by now, all these items must be considered early and cannot be easily bolted on as an afterthought. Choices of frameworks, libraries and page layout may end up quite different with this in mind.

If you take nothing else from this article, always remember to consider perceived load time and the impact it can have on your creations.

Freelance systems architect, developer and sysadmin

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade