openresty-minifiers

openresty-minifiers - A high-performance minifier library implemented by OpenResty Inc. that supports minification of HTML, CSS, and JavaScript files.

Name

openresty-minifiers - A high-performance minifier library implemented by OpenResty Inc. that supports minification of HTML, CSS, and JavaScript files.

The library requires the private library replace-filter-plus module.

Back to TOC

Table of Contents

Description

OpenResty Minifiers is composed of three distinct minifiers, each serving a unique purpose in the optimization of web content. These include:

  • JS Minifier: This component is responsible for the minification of JavaScript files. It removes unnecessary characters like white spaces, new lines, and comments, thereby reducing the file size and improving the load time of your web pages.
  • CSS Minifier: Similar to the JS Minifier, the CSS Minifier minifies CSS files. It optimizes the stylesheets by removing unnecessary characters and spaces, which results in faster page rendering.
  • HTML Minifier: The HTML Minifier minifies HTML files. It eliminates redundant HTML tags, white spaces, and comments, leading to reduced bandwidth usage and faster page load times.

These minifiers are proprietary Nginx output filter modules that support streaming processing. The time complexity is strictly O(n), where n is the length of the response body data stream. The space complexity is strictly O(1), meaning they use a constant amount of memory regardless of the input size.

Benchmark

They utilize a proprietary regular expression compiler based on our own DFA optimization algorithms.

In our benchmark, the JS-minifier module can achieve about 120+ MB/s scanning speed on a single CPU core of Core i9-13900K. It supports streaming processing using constant buffer sizes like 8KB.

Benchmark results can be viewed here: https://openresty.org/misc/re/bench/.

Back to TOC

Synopsis

Load the replace-filter-plus module in the http block of the OpenResty configuration file, and then load the openresty-minifiers module in the init_by_lua_block block.

http {
    ...
    load_module /usr/local/openresty/nginx/modules/ngx_http_replace_filter_module.so;

    init_by_lua_block { require "resty.replace" }
    ...

}

You can load min-js.so, min-html.so, min-css.so according to your needs, which can minimize js, html, css files respectively:

js-minifier

http {
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so
        /usr/local/openresty-minifiers/tpls/min-js.tpl;

    server {

        ...
        location ~ \.js$ {
            replace_filter_types application/javascript;
            replace_filter_max_buffered_size 8k;
            access_by_lua_block {
                local ok, err = require "resty.replace".pick("min-js")
                if not ok then
                    error("failed to pick replace prog: " .. err)
                end
            }
        }
    }
}

Back to TOC

html-minifier

http {
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so
        /usr/local/openresty-minifiers/tpls/min-html.tpl;

    server {

        ...

        location ~ \.html$ {
            replace_filter_types text/html;
            replace_filter_max_buffered_size 8k;
            access_by_lua_block {
                local ok, err = require "resty.replace".pick("min-html")
                if not ok then
                    error("failed to pick replace prog: " .. err)
                end
            }
        }
    }
}

Back to TOC

css-minifier

http {
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so
        /usr/local/openresty-minifiers/tpls/min-css.tpl;

    server {

        ...

        location ~ \.css$ {
            replace_filter_types text/css;
            replace_filter_max_buffered_size 8k;
            access_by_lua_block {
                local ok, err = require "resty.replace".pick("min-css")
                if not ok then
                    error("failed to pick replace prog: " .. err)
                end
            }
        }
    }
}

Back to TOC

Installation

Configure the software repository

First we need to configure the repository for the binary installer, follow the command below. (The CLIENT_TOKEN in the command needs to be replaced with a valid Token from the subscription email)

curl -o get-xray-priv-lib-repo.sh https://pkg2.openresty.com/scripts/get-xray-priv-lib-repo.sh

sudo bash get-xray-priv-lib-repo.sh -l openresty-minifiers -t CLIENT_TOKEN

Installing packages

Note: The following installation instructions are for OpenResty 1.21.4.x.

For operating systems using the yum package manager, run the following command to install the private library.

sudo yum install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4

For operating systems using the dnf package manager, execute the following command to install the private libraries.

sudo dnf install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4

For operating systems using the apt package manager, execute the following command to install the private libraries.

sudo apt-get install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4

Back to TOC

Directives

The following directives are available in the replace-filter-plus module, which is the dependency of the openresty-minifiers module.

Back to TOC

replace_filter_preload

syntax: replace_filter_preload <so_path> <templatefile>;

default: no

context: http


Load the or-regex pre-generated .so file and template file, and insert into the hash table during the init phase. Extract the filename of so_path as key, without the file suffix.

The example as follows:

http {
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so /usr/local/openresty-minifiers/tpls/min-html.tpl;
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so /usr/local/openresty-minifiers/tpls/min-css.tpl;
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so /usr/local/openresty-minifiers/tpls/min-js.tpl;
}

The filename of /usr/local/openresty-minifiers/lib/min-html.so is extracted to min-html as hashkey.

The template file format is as follows:

"$&" g
"$&" g
"" g

The quoting rules should follow the nginx config file’s string syntax or something similar.

Back to TOC

replace_filter_types

syntax: replace_filter_types <mime-type> …

default: replace_filter_types text/html

context: http, server, location, location if

phase: output body filter

Specify one or more MIME types (in the Content-Type response header) to be processed.

By default, only text/html typed responses are processed.

Programs explicitly selected through the Lua API bypass this check. The caller must select a program appropriate for the response type.

Back to TOC

replace_filter_max_buffered_size

syntax: replace_filter_max_buffered_size <size>

default: replace_filter_max_buffered_size 8k

context: http, server, location, location if

phase: output body filter

Limits the total size of the data buffered by the module at runtime. Default to 8k.

Back to TOC

replace_filter_last_modified

syntax: replace_filter_last_modifiled keep | clear

default: replace_filter_last_modified clear

context: http, server, location, location if

phase: output body filter

Controls how to deal with the existing Last-Modified response header.

By default, this module will clear the Last-Modified response header if there is any. You can specify

    replace_filter_last_modified keep;

to always keep the original Last-Modified response header.

Back to TOC

replace_filter_skip

syntax: replace_filter_skip value

default: no

context: http, server, location, location if

phase: output header filter

Controls whether to skip replacement for the current response. An empty string or "0" allows replacement; any other value skips it. Values may contain NGINX variables.

Back to TOC

API for Lua

pick() selects a preloaded program during the access phase; the module’s subsequent header filter decides whether to enable it. enable() checks the response and enables the program immediately in header_filter_by_lua_block, when the upstream response headers are available. Use it when selecting a minifier based on Content-Type.

Propertypick(name)enable(name)
Call phaseaccess_by_lua_blockheader_filter_by_lua_block
Use caseThe program is known during request processingSelection depends on response headers
Response checks and header adjustmentsPerformed later by the module’s header filterPerformed during the call
Success resulttrue: selected, but the response may still be skippedtrue: enabled
Skip or failure resultnil, err: selection failedfalse, reason: skipped; nil, err: failed

Response eligibility

  • Programs selected from Lua bypass replace_filter_types. The caller must choose a program appropriate for the response MIME type; this directive does not protect JSON, images, or other unsuitable responses from a Lua-selected program.
  • Responses with a nonempty Content-Encoding (such as gzip), Content-Length: 0, or a true replace_filter_skip value are skipped. Their bodies and relevant headers remain unchanged.
  • Enabling a program clears Content-Length and Accept-Ranges. It also clears Last-Modified by default, unless replace_filter_last_modified keep is configured.
  • Only main requests are processed. Subrequest bodies and headers pass through unchanged, including for subrequests with replace_filter configured. A body captured with ngx.location.capture() can still be replaced once when emitted by an enabled main request.
  • Only one program can be selected per request. Repeated enable(A) calls return true once A is enabled. Calling enable(A) after pick(A) is supported; if A is still disabled, response checks run before activation. Selecting a different program returns nil, "current request already set prog".

Use pick() when the program is known during the access phase. To select a program from response headers, call enable() in header_filter_by_lua_block; it checks the response and adjusts the response headers.

Back to TOC

find_prog_id(name)

Looks up the program ID using the filename loaded by replace_filter_preload, without the .so extension. For example, min-html.so is named min-html. name must be a string. Returns -1 when no program is found.

Back to TOC

set_prog_by_id(r, prog_id)

Selects a program during the access phase. r is the request pointer obtained from require("resty.core.base").get_request(), and prog_id is an ID returned by find_prog_id().

Returns nil on success, or an error string on failure. Subrequests return "subrequests are not supported" without selecting a program. Successful selection does not guarantee that the response will be modified; the subsequent response checks must still pass.

Back to TOC

pick(name)

A convenience interface that looks up the program ID and calls set_prog_by_id() for the current request. Only successful lookups are cached.

access_by_lua_block {
    local ok, err = require("resty.replace").pick("min-js")
    if not ok then
        ngx.log(ngx.ERR, "failed to pick minifier: ", err)
    end
}

Returns true on success, or nil, err on failure; it does not return false. Unknown names return nil, "replace program not found: <name>". Selecting a known program in a subrequest returns nil, "subrequests are not supported".

Back to TOC

enable(name)

Call only in header_filter_by_lua_block. It checks the current response, enables the program, and adjusts the response headers on success. The caller does not need to clear these headers manually.

Return valueMeaning
trueThe requested program is enabled, including when it was already enabled.
false, "empty response body"Content-Length is 0.
false, "response body is content-encoded"Content-Encoding is nonempty.
false, "replace_filter_skip is true"replace_filter_skip evaluates to true.
false, "subrequests are not supported"The current request is a subrequest.
nil, errUnknown program, a conflicting program, invalid call phase, or another error.

A false result selects no new program and changes no response headers; a program previously selected with pick() remains selected but disabled. Unknown names return replace program not found: <name>; an invalid call phase returns API disabled in the current context. Invalid argument types raise a Lua error.

Once the requested program is enabled, repeated calls return true without rechecking the response or adjusting headers again. The header-filter phase and unsent-header requirements still apply.

If no program was selected earlier, native replace_filter rules take precedence over enable() when they apply to the response, including inherited rules. The call returns nil, "current request already set prog" regardless of module order. If the response does not match replace_filter_types, the native rules do not prevent Lua activation. Skipped, content-encoded, and empty responses still return false, reason.

Back to TOC

enable_prog_by_id(r, prog_id)

The ID-based form of enable(). r and prog_id have the same meanings as in set_prog_by_id(). The call phase, response checks, and true / false, reason / nil, err return convention match enable(). An invalid ID returns nil, "invalid prog id".

Back to TOC

Selecting a minifier from response headers

This example requires a version that supports enable(), assumes the filter module is loaded, and assumes an upstream named backend is configured. It accepts MIME parameters such as text/css; charset=utf-8, skips unlisted types, and uses enable() to select a minifier from the final response headers. The upstream request clears Accept-Encoding; if the upstream still returns an encoded response, the module skips it.

http {
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so
        /usr/local/openresty-minifiers/tpls/min-html.tpl;
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so
        /usr/local/openresty-minifiers/tpls/min-css.tpl;
    replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so
        /usr/local/openresty-minifiers/tpls/min-js.tpl;

    server {
        location / {
            proxy_set_header Accept-Encoding "";
            proxy_pass http://backend;

            header_filter_by_lua_block {
                local content_type = ngx.header.content_type or ""
                local match = ngx.re.match(content_type, [[^\s*([^;\s]+)]], "jo")
                if not match then
                    return
                end

                local mime = string.lower(match[1])
                local name
                if mime == "text/html" then
                    name = "min-html"

                elseif mime == "text/css" then
                    name = "min-css"

                elseif mime == "text/javascript"
                       or mime == "application/javascript"
                then
                    name = "min-js"

                else
                    return
                end

                local ok, err = require("resty.replace").enable(name)
                if ok == nil then
                    ngx.log(ngx.ERR, "failed to enable minifier: ", err)
                end
            }
        }
    }
}

Back to TOC

Copyright (C) by OpenResty Inc. All rights reserved.

This software is proprietary and must not be redistributed or shared at all.

Back to TOC