Use minify-html, the extremely fast HTML + JS + CSS minifier, with Django.

django-minify-html

https://img.shields.io/github/workflow/status/adamchainz/django-minify-html/CI/main?style=for-the-badge https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge https://img.shields.io/pypi/v/django-minify-html.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

Use minify-html, the extremely fast HTML + JS + CSS minifier, with Django.

Requirements

Python 3.8 to 3.10 supported.

Django 2.2 to 4.0 supported.


Are your tests slow? Check out my book Speed Up Your Django Tests which covers loads of best practices so you can write faster, more accurate tests.


Installation

  1. Install with pip:

    python -m pip install django-minify-html
  2. Add django-minify-html to your INSTALLED_APPS:

    INSTALLED_APPS = [
        ...,
        "django_minify_html",
        ...,
    ]
  3. Add the middleware:

    MIDDLEWARE = [
        ...,
        "django_minify_html.middleware.MinifyHtmlMiddleware",
        ...,
    ]

    The middleware should be below any other middleware that may encode your responses, such as Django’s GZipMiddleware. It should be above any that may modify your HTML, such as those of django-debug-toolbar or django-browser-reload.

Reference

For information about what minify-html does, refer to its documentation.

django_minify_html.middleware.MinifyHtmlMiddleware

The middleware runs minify_html.minify() on the content of HTML responses. This function minifies HTML, and any inline JavaScript and CSS.

The middleware passes keyword arguments to minify() from its minify_args attribute, a dictionary of names to values. These correspond to the values in the Rust library’s Cfg structure, which have defaults in the Python library as visible in the source. By default the middleware overrides minify_css and minify_js to True. If you need to change an argument, subclass the middleware, replace minify_args, and use your subclass. For example, to preserve comments after minification:

from django_minify_html.middleware import MinifyHtmlMiddleware


class ProjectMinifyHtmlMiddleware:
    minify_args = MinifyHtmlMiddleware.minify_args | {
        "keep_comments": True,
    }

(This example uses Python 3.9’s dictionary merge operator.)

The middleware applies to all non-streaming, non-encoded HTML responses. To restrict this logic, you can subclass, override the should_minify() method, and use your subclass. The should_minify() method accepts the request and response, and returns a bool. For example, to avoid minification of URL’s with the URL prefix /admin/:

from django.http import HttpRequest, HttpResponse

from django_minify_html.middleware import MinifyHtmlMiddleware


class ProjectMinifyHtmlMiddleware:
    def should_minify(self, request: HttpRequest, response: HttpResponse) -> bool:
        return super().should_minify(request, response) and not request.path.startswith(
            "/admin/"
        )

Note that responses are minified even when DEBUG is True. This is recommended because HTML minification can reveal bugs in your templates, so it’s best to always work with your HTML as it will appear in production. Minified HTML is hard to read with “View Source” - it’s best to rely on the inspector in your browser’s developer tools.

Motivation

HTML minification is an underappreciated techinque for web optimization. It can yield significant savings, even on top of other tools like compression with Brotli or Gzip.

There is an existing package for HTML minification in Django, django-htmlmin. But it is much slower, since it does the minification in Python. At time of writing, it is also unmaintained, with no release since March 2019.

There are other minifiers out there, but in benchmarks minify-html surpasses them all. It’s a really well optimized and tested Rust library, and seems to be the best available HTML minifier.

Some CDN’s provide automatic minification, such as CloudFlare. This can be convenient, since it requires no application changes. But it adds some overhead: non-minified HTML has to first be transferred to the CDN, and the CDN has to parse the response, and recombine it. It also means that you don’t get to see the potential side effects of minification until your code is live. Overall it should be faster and more predictable to minify within Django, at the point of HTML generation.

Owner
Adam Johnson
🦄 @django technical board member 🇬🇧 @djangolondon co-organizer ✍ AWS/Django/Python Author and Consultant
Adam Johnson
Comments
  • Enabling MinifyHtmlMiddleware causes infinite loop

    Enabling MinifyHtmlMiddleware causes infinite loop

    Python Version

    3.9.2

    Django Version

    3.2.13

    Package Version

    1.3.0

    Description

    Hi!

    I've added MinifyHtmlMiddleware to my project but rendering a view results in an infinite loop until the browser times out. This only happens on a Debian 11 machine with uWSGI. Running the same project on Windows 11 works and the module works correctly.

    MIDDLEWARE = [
        "corsheaders.middleware.CorsMiddleware",
        "django.middleware.gzip.GZipMiddleware",
        "django_minify_html.middleware.MinifyHtmlMiddleware",
        "debug_toolbar.middleware.DebugToolbarMiddleware",
        "django.middleware.security.SecurityMiddleware",
        "whitenoise.middleware.WhiteNoiseMiddleware",
        "user_sessions.middleware.SessionMiddleware",
        "django.middleware.locale.LocaleMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.csrf.CsrfViewMiddleware",
        "corsheaders.middleware.CorsPostCsrfMiddleware",
        "x_forwarded_for.middleware.XForwardedForMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
        "django.contrib.sites.middleware.CurrentSiteMiddleware",
    ]
    

    Even with GZipMiddleware and CorsMiddleware disabled, the same issue is present.

    What could be the cause for this? The application does not crash and does not log anything.

  • Some issues I found

    Some issues I found

    Python Version

    3.10.2

    Django Version

    4.0.2

    Package Version

    1.0.0

    Description

    I discover 2 issues

    Break html output

    Using only the installation settings: https://github.com/adamchainz/django-minify-html#installation

    With this enabled

    image

    Without this, the original:

    image

    Break select option with empty value

    https://github.com/django-crispy-forms/crispy-bootstrap5/issues/96

    This issue is already reported: https://github.com/adamchainz/django-minify-html/issues/21

    Reporting this as I have to disabled it because I can't live with these 2 issues. Hope to use this library if I could 😁

  • minify_html strips empty attributes, which produces an error for django select widgets with blank=True

    minify_html strips empty attributes, which produces an error for django select widgets with blank=True

    Python Version

    3.9

    Django Version

    4.0

    Description

    Hey Adam 👋

    Quick FYI I noticed for you (although this is probably more of an unintended feature rather than a bug).

    When using minify_html on a Django template with a select widget that can accept a blank value, minify_html will strip the blank option, which means an exception is caught in the field clean method. E.g:

    class MyModel(models.Model):
        struct_org_type = models.CharField(
            default="",
            blank=True,
            max_length=255,
            choices=SCHEMA_ORG_CHOICES
        )
    

    Would normally render something like:

    <select id="id_struct_org_type" name="struct_org_type">
        <option value="" selected="">---------</option>
        <option value="example_1">Example 1</option>
        <option value="example_2">Example 2</option>
    </select>
    
    

    However, minify_html will strip the empty value="" attribute in the first option. Without this, django won't recognise the input as valid (being null rather than a blank str) if this is the selected option. It produces:

    Select a valid choice. --------- is not one of the available choices.

    I'm not sure there's any way around this, other than to wrap some conditional logic around should_minify() to prevent its use on views with forms, but thought it worth pointing out.

  • It seems we created the same middleware

    It seems we created the same middleware

    Hi there again,

    I created this repo some time ago. It also has Downloads and Downloads

    Which also has some traffic :

    python

    It essentially does what you are doing here (+ more). But there are some problems with this approach.

    As discussed here your approach is inefficient. ( Decoding GZIP is useless ). Also your approach doesn't work for special HTML tags like ( X-Init, X-Effect, X-On | Used by AlpineJS which is used in many Django project ).

    My approach here fixes some of the issues. Also to allow the usage in other applications I decided to split the main repo in another project.

    Also 2 projects for essentially one end goal is kinda useless. Because we will essentially rewrite same thing without adding any new features. Is there any chance of merging our repos together or focusing on one repo ( which will essentially allow this module to be used in every python application ) ?

    Currently my repo has an issue, which if fixed will be a feature complete library. I also have an idea of using esbuild with PyO3 ( which minify-html uses and with which i will replace minify-html ) to make the module even faster. I will also add full support for petite-vue in the next year. But for now the development is a bit slow as I am still a beginner in rust.

  • ending body & html tag missing

    ending body & html tag missing

    Python Version

    3.10

    Django Version

    4.1.2

    Package Version

    1.3.0

    Description

    ending and </html tags are missing, posting screenshots here... image Please reply...

  • Enabling minify brokes django-cms toolbar menu

    Enabling minify brokes django-cms toolbar menu

    If I enable MinifyHtmlMiddleware then this menu (page, Language) will make broken Screenshot 2022-09-01 at 08 03 07

    Not sure what data is cutting off with MinifyHtmlMiddleware is going to this result.

  • Issue installing it on Alpine image

    Issue installing it on Alpine image

    Python Version

    3.10.2

    Django Version

    4.0.2

    Package Version

    1.0.0

    Description

    I am creating the docker image of my app (I was developing it with python manage.py run server) and I want to use an alpine image, but I get this error message when I run pip install -r requirements.txt:

    ERROR: Could not find a version that satisfies the requirement minify-html (from django-minify-html) (from versions: none)
    ERROR: No matching distribution found for minify-html
    
  • Do we need the app?

    Do we need the app?

    Python Version

    3.10.2

    Django Version

    4.0.2

    Package Version

    1.0.0

    Description

    I installed the middleware but not the app and it is still working. Is the app necessary? What is the function of the app?

  • Bump actions/setup-python from 3 to 4

    Bump actions/setup-python from 3 to 4

    Bumps actions/setup-python from 3 to 4.

    Release notes

    Sourced from actions/setup-python's releases.

    v4.0.0

    What's Changed

    • Support for python-version-file input: #336

    Example of usage:

    - uses: actions/[email protected]
      with:
        python-version-file: '.python-version' # Read python version from a file
    - run: python my_script.py
    

    There is no default python version for this setup-python major version, the action requires to specify either python-version input or python-version-file input. If the python-version input is not specified the action will try to read required version from file from python-version-file input.

    • Use pypyX.Y for PyPy python-version input: #349

    Example of usage:

    - uses: actions/[email protected]
      with:
        python-version: 'pypy3.9' # pypy-X.Y kept for backward compatibility
    - run: python my_script.py
    
    • RUNNER_TOOL_CACHE environment variable is equal AGENT_TOOLSDIRECTORY: #338

    • Bugfix: create missing pypyX.Y symlinks: #347

    • PKG_CONFIG_PATH environment variable: #400

    • Added python-path output: #405 python-path output contains Python executable path.

    • Updated zeit/ncc to vercel/ncc package: #393

    • Bugfix: fixed output for prerelease version of poetry: #409

    • Made pythonLocation environment variable consistent for Python and PyPy: #418

    • Bugfix for 3.x-dev syntax: #417

    • Other improvements: #318 #396 #384 #387 #388

    Update actions/cache version to 2.0.2

    In scope of this release we updated actions/cache package as the new version contains fixes related to GHES 3.5 (actions/setup-python#382)

    Add "cache-hit" output and fix "python-version" output for PyPy

    This release introduces new output cache-hit (actions/setup-python#373) and fix python-version output for PyPy (actions/setup-python#365)

    The cache-hit output contains boolean value indicating that an exact match was found for the key. It shows that the action uses already existing cache or not. The output is available only if cache is enabled.

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • onepass variant support

    onepass variant support

    Description

    minify-html-onepass is now on PyPI: https://github.com/wilsonzlin/minify-html/issues/60

    We could adapt this package to support using it for those who want more speed.

Fully reponsive Chat Application built with django, javascript, materialUi, bootstrap4, html and css.
Fully reponsive Chat Application built with django, javascript, materialUi, bootstrap4, html and css.

Chat app (Full Stack Frameworks with Django Project) Fully reponsive Chat Application built with django, javascript, materialUi, bootstrap4, html and

Jan 19, 2022
This "I P L Team Project" is developed by Prasanta Kumar Mohanty using Python with Django web framework, HTML & CSS.
This

I-P-L-Team-Project This "I P L Team Project" is developed by Prasanta Kumar Mohanty using Python with Django web framework, HTML & CSS. Screenshots HO

Dec 15, 2021
Django-Text-to-HTML-converter - The simple Text to HTML Converter using Django framework

Django-Text-to-HTML-converter This is the simple Text to HTML Converter using Dj

Oct 9, 2022
Tweak the form field rendering in templates, not in python-level form definitions. CSS classes and HTML attributes can be altered.

django-widget-tweaks Tweak the form field rendering in templates, not in python-level form definitions. Altering CSS classes and HTML attributes is su

Nov 22, 2022
Basic Form Web Development using Python, Django and CSS

thebookrain Basic Form Web Development using Python, Django and CSS This is a basic project that contains two forms - borrow and donate. The form data

Nov 27, 2021
Compresses linked and inline javascript or CSS into a single cached file.

Django Compressor Django Compressor processes, combines and minifies linked and inline Javascript or CSS in a Django template into cacheable static fi

Dec 2, 2022
Packs a bunch of smaller CSS files together from 1 folder.

Packs a bunch of smaller CSS files together from 1 folder.

Dec 9, 2021
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites

django-quill-editor django-quill-editor makes Quill.js easy to use on Django Forms and admin sites No configuration required for static files! The ent

Nov 9, 2022
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Nov 18, 2021
django-tables2 - An app for creating HTML tables
django-tables2 - An app for creating HTML tables

django-tables2 - An app for creating HTML tables django-tables2 simplifies the task of turning sets of data into HTML tables. It has native support fo

Nov 30, 2022
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.
The best way to have DRY Django forms. The app provides a tag and filter that lets you quickly render forms in a div format while providing an enormous amount of capability to configure and control the rendered HTML.

django-crispy-forms The best way to have Django DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered

Dec 2, 2022
A Powerful HTML white space remover for Django

HTML Whitespace remover for Django Introduction : A powerful tool to optimize Django rendered templates Why use "django_stip_whitespace" ? Adds line b

Jan 1, 2022
An app that allows you to add recipes from the dashboard made using DJango, JQuery, JScript and HTMl.
An app that allows you to add recipes from the dashboard made using DJango, JQuery, JScript and HTMl.

An app that allows you to add recipes from the dashboard. Then visitors filter based on different categories also each ingredient has a unique page with their related recipes.

Jan 31, 2022
Fast / fuzzy PostgreSQL counts for Django

Created by Stephen McDonald Introduction Up until PostgreSQL 9.2, COUNT queries generally required scanning every row in a database table. With millio

Oct 25, 2021
Django-fast-export - Utilities for quickly streaming CSV responses to the client

django-fast-export Utilities for quickly streaming CSV responses to the client T

Aug 24, 2022
Transparently use webpack with django

Looking for maintainers This repository is unmaintained as I don't have any free time to dedicate to this effort. If you or your organisation are heav

Dec 2, 2022
A Django application that provides country choices for use with forms, flag icons static files, and a country field for models.

Django Countries A Django application that provides country choices for use with forms, flag icons static files, and a country field for models. Insta

Dec 4, 2022
Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards and optional settings files.
Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards and optional settings files.

Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards in settings file paths and mark setti

Nov 27, 2022
Full-text multi-table search application for Django. Easy to install and use, with good performance.

django-watson django-watson is a fast multi-model full-text search plugin for Django. It is easy to install and use, and provides high quality search

Dec 4, 2022