static: Pre-compress with zopfli, for better compression.

Zopfli[^1] performs very good, but time-intensive, zlib compression.
It is hence only suitable for pre-compressing objects, not on-the-fly
compression.

Use a webpack plugin to write pre-compressed versions of JS and CSS
assets using Zopfli, and configure nginx to serve those assets when
`Accept-Encoding: gzip` is provided.

This reduces the size of the JS and CSS assets on initial pageload
from 1422872 bytes to 1108267 bytes, or about a 22% savings.

[^1]: https://github.com/google/zopfli
This commit is contained in:
Alex Vandiver 2024-07-31 22:10:40 +00:00 committed by Tim Abbott
parent b89d47a147
commit 2840e68548
5 changed files with 38 additions and 1 deletions

View File

@ -8,6 +8,7 @@
"@babel/register": "^7.6.2", "@babel/register": "^7.6.2",
"@fontsource-variable/open-sans": "^5.0.9", "@fontsource-variable/open-sans": "^5.0.9",
"@formatjs/intl": "^2.0.0", "@formatjs/intl": "^2.0.0",
"@gfx/zopfli": "^1.0.15",
"@giphy/js-components": "^5.13.0", "@giphy/js-components": "^5.13.0",
"@giphy/js-fetch-api": "^5.6.0", "@giphy/js-fetch-api": "^5.6.0",
"@koa/bodyparser": "^5.0.0", "@koa/bodyparser": "^5.0.0",
@ -25,6 +26,7 @@
"clean-css": "^5.1.0", "clean-css": "^5.1.0",
"clipboard": "^2.0.4", "clipboard": "^2.0.4",
"colord": "^2.9.3", "colord": "^2.9.3",
"compression-webpack-plugin": "^11.1.0",
"core-js": "^3.37.0", "core-js": "^3.37.0",
"css-loader": "^7.1.1", "css-loader": "^7.1.1",
"css-minimizer-webpack-plugin": "^7.0.0", "css-minimizer-webpack-plugin": "^7.0.0",

View File

@ -37,6 +37,9 @@ importers:
'@formatjs/intl': '@formatjs/intl':
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.10.4(typescript@5.5.2) version: 2.10.4(typescript@5.5.2)
'@gfx/zopfli':
specifier: ^1.0.15
version: 1.0.15
'@giphy/js-components': '@giphy/js-components':
specifier: ^5.13.0 specifier: ^5.13.0
version: 5.13.0(@babel/core@7.24.9) version: 5.13.0(@babel/core@7.24.9)
@ -88,6 +91,9 @@ importers:
colord: colord:
specifier: ^2.9.3 specifier: ^2.9.3
version: 2.9.3 version: 2.9.3
compression-webpack-plugin:
specifier: ^11.1.0
version: 11.1.0(webpack@5.93.0(webpack-cli@5.1.4))
core-js: core-js:
specifier: ^3.37.0 specifier: ^3.37.0
version: 3.37.1 version: 3.37.1
@ -1785,6 +1791,10 @@ packages:
'@gar/promisify@1.1.3': '@gar/promisify@1.1.3':
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
'@gfx/zopfli@1.0.15':
resolution: {integrity: sha512-7mBgpi7UD82fsff5ThQKet0uBTl4BYerQuc+/qA1ELTwWEiIedRTcD3JgiUu9wwZ2kytW8JOb165rSdAt8PfcQ==}
engines: {node: '>= 8'}
'@giphy/js-analytics@5.0.0': '@giphy/js-analytics@5.0.0':
resolution: {integrity: sha512-jBZG6OqyMWB6meLi8Sz3iLplXYnhkbj+DJhS4ChmRX8Y6UA7i5dbbsUN/So1s7tTjhZOvu0rxA6rWJE73S1FvQ==} resolution: {integrity: sha512-jBZG6OqyMWB6meLi8Sz3iLplXYnhkbj+DJhS4ChmRX8Y6UA7i5dbbsUN/So1s7tTjhZOvu0rxA6rWJE73S1FvQ==}
@ -3555,6 +3565,12 @@ packages:
resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
compression-webpack-plugin@11.1.0:
resolution: {integrity: sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA==}
engines: {node: '>= 18.12.0'}
peerDependencies:
webpack: ^5.1.0
compression@1.7.4: compression@1.7.4:
resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -10709,6 +10725,10 @@ snapshots:
'@gar/promisify@1.1.3': {} '@gar/promisify@1.1.3': {}
'@gfx/zopfli@1.0.15':
dependencies:
base64-js: 1.5.1
'@giphy/js-analytics@5.0.0': '@giphy/js-analytics@5.0.0':
dependencies: dependencies:
'@giphy/js-types': 5.1.0 '@giphy/js-types': 5.1.0
@ -12793,6 +12813,12 @@ snapshots:
dependencies: dependencies:
mime-db: 1.53.0 mime-db: 1.53.0
compression-webpack-plugin@11.1.0(webpack@5.93.0(webpack-cli@5.1.4)):
dependencies:
schema-utils: 4.2.0
serialize-javascript: 6.0.2
webpack: 5.93.0(webpack-cli@5.1.4)
compression@1.7.4: compression@1.7.4:
dependencies: dependencies:
accepts: 1.3.8 accepts: 1.3.8

View File

@ -9,6 +9,7 @@ error_page 502 503 504 /static/webpack-bundles/5xx.html;
# Serve static files directly # Serve static files directly
location /static/ { location /static/ {
alias /home/zulip/prod-static/; alias /home/zulip/prod-static/;
gzip_static on;
include /etc/nginx/zulip-include/headers; include /etc/nginx/zulip-include/headers;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Timing-Allow-Origin *; add_header Timing-Allow-Origin *;

View File

@ -50,4 +50,4 @@ API_FEATURE_LEVEL = 280 # Last bumped for can_create_web_public_channel_group
# historical commits sharing the same major version, in which case a # historical commits sharing the same major version, in which case a
# minor version bump suffices. # minor version bump suffices.
PROVISION_VERSION = (290, 0) # bumped 2024-08-12 for upgrading starlight to latest version. PROVISION_VERSION = (290, 1) # bumped 2024-08-12 to add zopfli compression

View File

@ -2,6 +2,9 @@
import path from "path"; import path from "path";
import type {ZopfliOptions} from "@gfx/zopfli";
import {gzip} from "@gfx/zopfli";
import CompressionPlugin from "compression-webpack-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin"; import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin"; import MiniCssExtractPlugin from "mini-css-extract-plugin";
@ -218,6 +221,11 @@ const config = (
chunks: ["error-styles"], chunks: ["error-styles"],
publicPath: production ? "/static/webpack-bundles/" : "/webpack/", publicPath: production ? "/static/webpack-bundles/" : "/webpack/",
}), }),
new CompressionPlugin<ZopfliOptions>({
// Use zopfli to write pre-compressed versions of text files
test: /\.(js|css|html)$/,
algorithm: gzip,
}),
], ],
devServer: { devServer: {
client: { client: {