Adsar Logo


PHP HTTP performance tricks



There are a few lesser-known features in PHP that you can help with regards to web page performance.
I thought I'd share my favourite three in case these can help you with your project!

Tip 1 - fastcgi_finish_request

If you are using PHP-FPM with Apache or nginx then fastcgi_finish_request is a great function.
It effectively tells the web server that you've finished processing the user's request, so it returns the page to the user.
However any code written after this point is still executed, so here is where you can put any code not relating to the page, for example, logging or other database activity.
This can help to shave 100s of ms off the page load for some apps.

ignore_user_abort(true);
fastcgi_finish_request();

Tip 2 - HTTP 2 server push

If you are using HTTP 2 to serve content, then server push can help with page load.
If you are using Cloudflare then you can add Link headers in your page, and Cloudflare will push content to the browser's cache at the same time as the HTML content.
So, instead of the browser downloading the HTML, parsing it, and requesting your CSS, JS, images, etc, you can automatically send them to the browser, knowing that it'll need it soon.

As you don't want to be pushing unnecessary content, we typically only do server push when isn't navigating between pages on our domain.

if (substr($_SERVER['HTTP_REFERER'], 0, y) != 'DOMAIN') {
    header("Link: </css/main.css>; rel=preload; as=style", false);
    header("Link: </js/main.js>; rel=preload; as=script", false);
}

Tip 3 - Flush your page early

If your page does some number crunching or database work, then it makes sense to flush what you've produced so far to the browser so it can start to render part of the page, whilst you crunch the rest.
I typically use the below curated flushPage() function that makes sure that the buffer is big enough to flush.
You also need to enable "flushpackets=on" on Apache 2.4 to allow it to work.

function flushPage()
{
    // Pad buffer
    echo str_pad('', ini_get('output_buffering'));

    // Flush
    do {
        $flushed = @ob_end_flush();
    } while ($flushed);

    @ob_flush();
    flush();
}
<FilesMatch \.(php|phar)$>
    SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
</FilesMatch>
<Proxy fcgi://localhost>
    ProxySet enablereuse=off flushpackets=on
</Proxy>

I've added a very crude example below, where we have 5 seconds worth of computation in the page.
So, there's waterfalls showing the page load before and after we turned on flushing. (the green line indicates the point at which page rendering happened)

waterfall.1.png
waterfall.2.png



Trees for life


Want to get in touch? mail@adsar.co.uk