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.
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.
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.
Remember to also disable PHP output compression.
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.
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.
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.
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.
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.
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.