'How to minify Symfony / Twig generated HTML code to satisfy Google PageSpeed Insight?

When testing my Symfony 2.8 based webpage with Google PageSpeed Insights I am getting warned, that the HTML code is not minified.

This is true, while Assetic takes care of minifying all CSS files and JS scripts, the HTML code itself is quite dirty.

Google recommends to use HTML Minifier, but since this a JavaScript tools it cannot be used as Twig extension, filter, etc., can it?

The Twig docu of the spaceless tag makes clear, that this tag is not meant to minify HTML and furthmore:

If you want to create a tag that actually removes all extra whitespace in an HTML string, be warned that this is not as easy as it seems to be (think of textarea or pre tags for instance). Using a third-party library like Tidy is probably a better idea.

But again I don't see how Tidy could be integrated into the Twig templates, etc.

So, what is the best way, to create minified HTML output with Symfony and Twig?



Solution 1:[1]

This is a good question and there isn't a flagship bundle but after a quick search, you have two bundle which could help you:

SemaMinifierBundle

This bundle allows you to minify in one config value all your responses (automatically on KernelResponse event) or, on demand, with a twig extension.

But this bundle is pretty old (3 years) and is not ready for Symfony3.

HtmlCompressorBundle

This bundle is a bit more recent and use the htmlcompressor library.

Solution 2:[2]

To achieve this several years later, i created a Subscriber on kernel.response event.

# src/Event/MinificationSubscriber.php
        
<?php

namespace App\Event;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class MinificationSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::RESPONSE => ['onKernelResponse', -256]
        ];
    }

    public function onKernelResponse($event)
    {
        if (
            $event->getRequestType() != HttpKernelInterface::MAIN_REQUEST
            || $event->getRequest()->get('_route') === 'admin' // don't apply on admin pages
        ) {
            return;
        }
        $response = $event->getResponse();
        $buffer = $response->getContent();

        $replace = [
            '/<!--[^\[](.*?)[^\]]-->/s' => '',
            "/<\?php/" => '<?php ',
            "/\n([\S])/" => '$1',
            "/\r/" => '',
            "/\n/" => '',
            "/\t/" => '',
            '/ +/' => ' ',
        ];

        if (false !== strpos($buffer, '<pre>')) {
            $replace = [
                '/<!--[^\[](.*?)[^\]]-->/s' => '',
                "/<\?php/" => '<?php ',
                "/\r/" => '',
                "/>\n</" => '><',
                "/>\s+\n</" => '><',
                "/>\n\s+</" => '><',
            ];
        }

        $buffer = preg_replace(array_keys($replace), array_values($replace), $buffer);

        $response->setContent($buffer);
    }
}

Based on this post, this other one and this gist.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 lenybernard
Solution 2