How do I compress HTML in laravel 5

27,648

Solution 1

The recommended way to do this in Larvel 5 is to rewrite your function as middleware. As stated in the docs:

..this middleware would perform its task after the request is handled by the application:

<?php namespace App\Http\Middleware;

class AfterMiddleware implements Middleware {

    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

Solution 2

Complete code is this (with custom GZip enabled) :

<?php

namespace App\Http\Middleware;

use Closure;

class OptimizeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $buffer = $response->getContent();
        if(strpos($buffer,'<pre>') !== false)
        {
            $replace = array(
                '/<!--[^\[](.*?)[^\]]-->/s' => '',
                "/<\?php/"                  => '<?php ',
                "/\r/"                      => '',
                "/>\n</"                    => '><',
                "/>\s+\n</"                 => '><',
                "/>\n\s+</"                 => '><',
            );
        }
        else
        {
            $replace = array(
                '/<!--[^\[](.*?)[^\]]-->/s' => '',
                "/<\?php/"                  => '<?php ',
                "/\n([\S])/"                => '$1',
                "/\r/"                      => '',
                "/\n/"                      => '',
                "/\t/"                      => '',
                "/ +/"                      => ' ',
            );
        }
        $buffer = preg_replace(array_keys($replace), array_values($replace), $buffer);
        $response->setContent($buffer);
        ini_set('zlib.output_compression', 'On'); // If you like to enable GZip, too!
        return $response;
    }
}

Please check your browser network inspector for Content-Length header before/after implement this code.

enjoy it ... :).. .

Solution 3

It is not very good solution to minify html in middleware as you can spend a lot of CPU time on it and it runs on every request.

Instead it is better to use htmlmin package ( https://github.com/HTMLMin/Laravel-HTMLMin ):

composer require htmlmin/htmlmin
php artisan vendor:publish

Minifying HTML on blade template level and caching it in storage should be much more effective.

Solution 4

I have created a webpack plugin to solve same purpose.MinifyHtmlWebpackPlugin

Install the plugin with npm:

$ npm install minify-html-webpack-plugin --save-dev

For Laravel Mix Users

Paste below snippets into mix.js file.

    const MinifyHtmlWebpackPlugin = require('minify-html-webpack-plugin');
    const mix = require('laravel-mix');

    mix.webpackConfig({
        plugins: [
            new MinifyHtmlWebpackPlugin({
                src: './storage/framework/views',
                ignoreFileNameRegex: /\.(gitignore)$/,
                rules: {
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    removeComments: true,
                    minifyJS: true,
                }
            })
        ]
    });

It will minify all view files during the Webpack build.

Solution 5

This is almost a copy of Vahid's answer but it fixes two problems.

1) It checks if a response is a BinaryFileResponse as any attempt to modify this type of response will throw an Exception.

2) It retained newline characters as the complete elimination of newlines will lead to bad Javascript code on lines with single-line comment.

For example, the code below

 var a; //This is a variable
 var b; //This will be commented out

Will become

 var a; //This is a variable var b; //This will be commented out

Note: At the time of this answer I couldn't get my hands on a good regex to match single line comments without complications or rather, ignore newlines on only lines with a single-line comment, so I'm hoping for a better fix.

Here's the modified version.

<?php

namespace App\Http\Middleware;

use Closure;

class OptimizeMiddleware {

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    $response = $next($request);
    if ($response instanceof \Symfony\Component\HttpFoundation\BinaryFileResponse) {
        return $response;
    } else {
        $buffer = $response->getContent();
        if (strpos($buffer, '<pre>') !== false) {
            $replace = array(
                '/<!--[^\[](.*?)[^\]]-->/s' => '',
                "/<\?php/" => '<?php ',
                "/\r/" => '',
                "/>\n</" => '><',
                "/>\s+\n</" => '><',
                "/>\n\s+</" => '><',
            );
        } else {
            $replace = array(
                '/<!--[^\[](.*?)[^\]]-->/s' => '',
                "/<\?php/" => '<?php ',
                "/\n([\S])/" => '$1',
                "/\r/" => '',
                "/\n+/" => "\n",
                "/\t/" => '',
                "/ +/" => ' ',
            );
        }
        $buffer = preg_replace(array_keys($replace), array_values($replace), $buffer);
        $response->setContent($buffer);
        ini_set('zlib.output_compression', 'On'); //enable GZip, too!
        return $response;
    }
  }
}

Edit

Compressing output for every request using the middleware truly is really a bad idea, I recommend you check out this solution by Jokerius

Share:
27,648

Related videos on Youtube

Emeka Mbah
Author by

Emeka Mbah

I am a professional Full Stack Web Developer with over 10 years expertise in professional web application development; 6 years as Full Stack PHP/Laravel Developer with track record of tackling challenges, providing solutions and beating deadlines. I used: PHP, MySQL, Laravel, Angular, JQuery, Vue, GulpJs, TDD, HTML, CSS &amp; CSS3, MySQL, Linux, Apache, API, Bootstrap, Git, Html5, Nginx, SVN, Team leadership, WordPress I am available for hire for projects or permanent employment.

Updated on March 17, 2021

Comments

  • Emeka Mbah
    Emeka Mbah about 3 years

    In Laravel 4.0, I use the code below to compress the HTML laravel response outputs to browser, however it doesn't work in laravel 5.

    App::after(function($request, $response)
    {
        if($response instanceof Illuminate\Http\Response)
        {
            $buffer = $response->getContent();
            if(strpos($buffer,'<pre>') !== false)
            {
                $replace = array(
                    '/<!--[^\[](.*?)[^\]]-->/s' => '',
                    "/<\?php/"                  => '<?php ',
                    "/\r/"                      => '',
                    "/>\n</"                    => '><',
                    "/>\s+\n</"                 => '><',
                    "/>\n\s+</"                 => '><',
                );
            }
            else
            {
                $replace = array(
                    '/<!--[^\[](.*?)[^\]]-->/s' => '',
                    "/<\?php/"                  => '<?php ',
                    "/\n([\S])/"                => '$1',
                    "/\r/"                      => '',
                    "/\n/"                      => '',
                    "/\t/"                      => '',
                    "/ +/"                      => ' ',
                );
            }
            $buffer = preg_replace(array_keys($replace), array_values($replace), $buffer);
            $response->setContent($buffer);
        }
    });
    

    Please how do i make this work in Laravel 5.

    OR

    Please provide a better way of compressing HTML in laravel 5 if any. Thanks in advance.

    NB: I don't wish to use any laravel package for compressing html, just need a simple code that does the work without killing performance.

    • Laurence
      Laurence about 9 years
      I know you said you dont want a pacakge - but github.com/GrahamCampbell/Laravel-HTMLMin is the perfect solution. And it wont 'kill performance' any differently than if you did it yourself.
    • CBroe
      CBroe about 9 years
      I would advise against what you are trying to do at all – a pre element is not the only thing where white space might matter, but also inside a textarea/input or basically in any element if it gets later on formatted via CSS (white-space). Just GZip the output before sending it to the client, that is much more effective than messing with the HTML code itself.
    • Emeka Mbah
      Emeka Mbah about 9 years
      @cbroe how do I use GZip? any working example
  • Emeka Mbah
    Emeka Mbah about 9 years
    Nice. How do I use it? Am running this on each request?
  • darronz
    darronz about 9 years
    Yes you'd run it on every http request. You can register it globally by adding your class in app/Http/Kernel.php
  • clearlight
    clearlight over 7 years
    Code snippet doesn't seem to run.
  • Rahul Tathod
    Rahul Tathod over 5 years
    (index):1 Uncaught SyntaxError: Unexpected end of input jquery not working after that
  • نرم افزار حضور و غیاب
    نرم افزار حضور و غیاب over 5 years
    @Rahul Tathod This middleware will only compress HTML, and enable GZip. These two compresses doesn't effect on Jquery, or CSS, JS files. If there is any problem, you should find root cause.
  • Holonaut
    Holonaut about 4 years
    will this still work if there are variables in your template that need to be accounted for on every request? Or even whole blocks that depend on conditions?
  • ahinkle
    ahinkle over 2 years
    2021 update: this package seems to have a lot of bugs and doesn't seem to be maintained.