Slide 1

Slide 1 text

HTTP Compression Improve performance of your Symfony apps Photo by Anna Evans

Slide 2

Slide 2 text

Kévin Dunglas ➔ Co-founder of Les-Tilleuls.coop ➔ Symfony Core Team ➔ Creator of API Platform, FrankenPHP and Mercure ➔ Maintainer of Caddy @dunglas

Slide 3

Slide 3 text

Compression?!

Slide 4

Slide 4 text

The process of encoding information using fewer bits than the original representation. WIKIPEDIA What is compression?

Slide 5

Slide 5 text

Why is Compression Useful for Web Apps? ➔ Reduces the size of resources to be transmitted ➔ Can reduce the time needed to download (or upload) a resource through a network ➔ Saves bandwidth ➔ Key factor in website performance (HTTP responses can be compressed) Photo by Denny Müller

Slide 6

Slide 6 text

Efficiency Depending on the file, the compression format and level, compression can reduce the size of a file from 2 to 3 times

Slide 7

Slide 7 text

➔ Space-time tradeoff: ● compressing and decompressing takes computational resources (CPU, RAM) ● and (some) time to execute the algorithm ➔ In the HTTP context, compression is useful if: ● it decreases latency (the time required for encoding/decoding is less than the time required to download the deleted data) ● additional computation costs do not exceed saved bandwidth costs Compression is a Tradeoff Photo by Mathew Schwartz

Slide 8

Slide 8 text

How Does Compression Work?

Slide 9

Slide 9 text

Lossy or Lossless Lossless compression: ➔ no loss of information after decoding ➔ ZIP, gzip, Brotli, PNG, GIF… ➔ Best for HTML, JS, CSS, SVG ➔ Focus of this talk Lossy compression: ➔ Inexact approximation ➔ data (quality) loss ➔ JPEG, MPEG, MP3…

Slide 10

Slide 10 text

Useful for most HTTP responses (but not all, wait for it!) Two popular techniques: ➔ Huffman coding assigns the shortest possible code to each transferred symbol, or group of symbols ➔ Static dictionaries share symbols between the client and the server Some formats support custom dictionaries, but it’s not supported by browsers yet They can be combined! Lossless Compression Photo by Niklas Ohlrogge

Slide 11

Slide 11 text

Huffman Tree (1952) © CMG Lee for Wikipedia

Slide 12

Slide 12 text

➔ Define how hard the compressor looks for matching strings (how many symbols to use in the Huffman tree) ➔ The higher the level: ● the smaller the final size ● the most time-consuming and CPU-intensive ➔ Remember: compression is a tradeoff Compression Levels Photo by Florian Hahn

Slide 13

Slide 13 text

Words from Brotli's dictionary (13504 in all) time जानकारी the University of

Slide 14

Slide 14 text

Compression and HTTP

Slide 15

Slide 15 text

© MDN HTTP Compression

Slide 16

Slide 16 text

➔ zstd (Zstandard), Facebook, 2016 ➔ br: (Brotli), Google, 2013 ➔ gzip, GNU, 1996 ➔ deflate, GNU, 1996 ➔ compress, Lempel-Ziv-Welch, 1984 ➔ identity, no compression or modification Supported Formats in Accept-Encoding Photo by Maksym Kaharlytskyi

Slide 17

Slide 17 text

Benchmarks Compressor name Ratio Compression Decompression zstd 1.5.6 -1 2.887 510 MB/s 1580 MB/s zlib 1.2.11 -1 2.743 95 MB/s 400 MB/s brotli 1.0.9 -0 2.702 395 MB/s 430 MB/s Source: Facebook

Slide 18

Slide 18 text

A surprising amount of sites are using low level gzip compression, and should consider increasing their compression levels. ➔ For dynamic content ○ Brotli level 5 usually results in smaller payloads, at similar or slightly slower compression times. ○ zStandard level 12 often produces similar payloads to Brotli level 5, with compression times faster than gzip and Brotli. ➔ For static content ○ Brotli level 11 produces the smallest payloads ○ zStandard is able to apply their highest compression levels much faster than Brotli, but the payloads are still smaller with Brotli. PAUL CAVANO - Choosing Between gzip, Brotli and zStandard Compression (19 Mar 2024) State of the art

Slide 19

Slide 19 text

Browser Support: Gzip (and so Zopfli) ✅ supported by all browsers, proxies and CDN

Slide 20

Slide 20 text

Browser Support: Brotli (98%) Source: MDN / caniuse

Slide 21

Slide 21 text

Browser Support: Zstandard (70%) Source: MDN / caniuse

Slide 22

Slide 22 text

Going further

Slide 23

Slide 23 text

Real-World HTTP Compression

Slide 24

Slide 24 text

What to compress? ➔ Everything… ➔ …except formats already compressed ● JPEG, PNG, GIF ● WebP (sometimes) ● WOFF/WOFF2 fonts ● XLSX, DOCX, PPTX… ● PDF (sometimes) ● ZIP, gz, bz2, RAR… ● binaries (sometimes) ➔ especially efficient with text files Photo by Glenn Carstens-Peters

Slide 25

Slide 25 text

Dynamic Content

Slide 26

Slide 26 text

➔ “on the fly” compression ➔ Compression can be done by: ● PHP itself (not my recommendation) ● The origin web server ● The CDN ➔ Prefer Zstandard ● Fallback on Brotli ○ Fallback on gzip ➔ Use a fast compression level to not increase latency Dynamic Content (HTML, JSON…) Photo by Crystal Kwok

Slide 27

Slide 27 text

FrankenPHP / Caddy (Dynamic Content) encode zstd br gzip # br is natively supported by FrankenPHP. # With Caddy, you need github.com/dunglas/caddy-cbrotli # (uses cgo)

Slide 28

Slide 28 text

NGINX (gzip, Dynamic Content) gzip on; # http://blog.klauspost.com/gzip-performance-for-go-webservers/ gzip_comp_level 5; # text/html is always compressed gzip_types text/css text/javascript; # ... gzip_proxied no-cache no-store private expired auth; gzip_min_length 512; gzip_vary on;

Slide 29

Slide 29 text

NGINX (Brotli, Dynamic Content) # Needs https://github.com/google/ngx_brotli brotli on; brotli_comp_level 5; brotli_types text/css text/javascript; # ...

Slide 30

Slide 30 text

Apache (gzip, Dynamic Content) SetOutputFilter DEFLATE DeflateCompressionLevel 5 AddOutputFilterByType DEFLATE text/html text/css text/javascript Header append Vary User-Agent

Slide 31

Slide 31 text

Apache (Brotli, Dynamic Content) SetOutputFilter BROTLI_COMPRESS DeflateCompressionLevel 5 AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/javascript Header append Vary User-Agent

Slide 32

Slide 32 text

Apache (Zstandard, Dynamic Content) ❌ not available

Slide 33

Slide 33 text

Dynamic Content: Misc

Slide 34

Slide 34 text

BREACH Attack (2012) ➔ Compression side-channel attack ➔ Data can leak, even if the content is encrypted with TLS ➔ To be affected, the page must: ● Be compressed ● Reflect user data (POST, query param…) ● Contain a secret that doesn’t change at each request ➔ Symfony CSRF tokens are safe by default ➔ The new stateless CSRF feature introduced in Symfony 7.2 is perfect for this use case Photo by Chris Lynch

Slide 35

Slide 35 text

Is your CDN modifying the response? (ex: CloudFlare proxy, WAF, optimizers…) ➔ yes ● Don’t compress, or compress as a low (fast) level ● Your CDN will decompress the response from the origin server, then re-compress it ➔ no ● Compress as usual, the CDN will just proxy the response CDN and Dynamic Content Photo by Diana Polekhina

Slide 36

Slide 36 text

Static Content

Slide 37

Slide 37 text

➔ Precompress! ➔ Use the most efficient (slowest) level ● Compressed only one time ➔ Prefer Brotli ● Fallback on Zopfli, a reimplementation of Gzip slower but more efficient Static files (JS, CSS, SVG, static HTML…) Photo by Nicolas Picard

Slide 38

Slide 38 text

How to Precompress?

Slide 39

Slide 39 text

Config framework: asset_mapper: precompress: enabled: true

Slide 40

Slide 40 text

Usage bin/console asset-map:compile # All assets will be compressed in Brotli, Zstandard and Gzip # The extension is appended to the original file name: # foo.js foo.js.br foo.js.zst foo.js.gz bin/console asset:compress robots.txt # The file passed as argument is compressed # robots.txt robots.txt.br robots.txt.zst robots.txt.gz # A asset_mapper.compressor is also available (ex: file upload)

Slide 41

Slide 41 text

➔ Webpack: compression-webpack-plugin ➔ Rollup / Vite: rollup-plugin-gzip ➔ Both support Brotli, Zopfli and gzip, but not Zstandard JavaScript Tools

Slide 42

Slide 42 text

FrankenPHP / Caddy (Static Content) file_server { precompressed }

Slide 43

Slide 43 text

NGINX (Static Content) gzip_static on; gzip_proxied expired no-cache no-store private auth; # Needs https://github.com/google/ngx_brotli brotli_static on # Needs https://github.com/tokers/zstd-nginx-module zstd_static on;

Slide 44

Slide 44 text

Apache (Static Content) AddEncoding gzip .gz AddEncoding br .br # Zstandard is not supported RewriteCond %{HTTP:Accept-encoding} gzip RewriteCond %{REQUEST_FILENAME}\.gz -s RewriteRule ^(.*)\ $1\.gz [QSA] RewriteCond %{HTTP:Accept-encoding} br RewriteCond %{REQUEST_FILENAME}\.br -s RewriteRule ^(.*)\ $1\.br [QSA] # Serve correct content types, and prevent double compression. RewriteRule \.css\.gz$ - [T=text/css,E=no-brotli:1] RewriteRule \.js\.gz$ - [T=text/javascript,E=no-brotli:1] RewriteRule \.css\.br$ - [T=text/css,E=no-gzip:1] RewriteRule \.js\.br$ - [T=text/javascript,E=no-brotli:1] # ...

Slide 45

Slide 45 text

➔ Compression is one of the easiest and most efficient ways to improve webperf, use it! ➔ Compress dynamic content using Zstandard ➔ Pre-compress static content with Brotli at the highest level (Use the new Symfony AssetMapper pre-compression feature) ➔ Using FrankenPHP and Caddy is the easiest way to serve compressed responses with modern efficient formats! Take Away

Slide 46

Slide 46 text

Web and Cloud Experts ➔ Dev: PHP, JS, Go, Rust... ➔ DevOps and SRE ➔ Consultancy and maintenance ➔ Agile management, UX and UI design… ➔ [email protected] 💌

Slide 47

Slide 47 text

Thank you! ➔ Come at our booth for swag, questions and discussions! les-tilleuls.coop [email protected] @coopTilleuls