From 3ef1744c1b3fbf7545f83ffb57c0e38634030d4c Mon Sep 17 00:00:00 2001 From: realrellek <91732532+realrellek@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:56:14 +0100 Subject: [PATCH] Feat: brotli support for precompressed files --- rocket-nginx.ini.disabled | 7 ++++ rocket-nginx.tmpl | 70 ++++++++++++++++++++++++++------------- rocket-parser.php | 16 +++++++++ 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/rocket-nginx.ini.disabled b/rocket-nginx.ini.disabled index 1aa8708..4608a44 100644 --- a/rocket-nginx.ini.disabled +++ b/rocket-nginx.ini.disabled @@ -23,6 +23,13 @@ wp_content_folder = wp-content ; Cache control header to use for html files html_cache_control = "no-cache, no-store, must-revalidate" +; Use brotli precompressed files +; Leave empty to not use brotli +; Otherwise, specify your file suffix for pre-compressed brotli versions +; such as ".br". +; Default: (empty) +brotli_suffix = "" + ; Cookies ; Cookies can be specified to avoid serving a cached file. Some plugins may need to bypass the use of a ; file that was previously cached. List all cookies that must invalidate the use of cache. diff --git a/rocket-nginx.tmpl b/rocket-nginx.tmpl index 47b7831..1920bbb 100644 --- a/rocket-nginx.tmpl +++ b/rocket-nginx.tmpl @@ -23,7 +23,7 @@ set $rocket_debug #!# DEBUG #!#; # Do not alter theses values # set $rocket_bypass 1; # Should NGINX bypass WordPress and call cache file directly ? -set $rocket_encryption ""; # Is GZIP accepted by client ? +set $rocket_encryption ""; # Is GZIP/BR accepted by client ? set $rocket_file ""; # Filename to look for set $rocket_is_bypassed "MISS"; # Header text added to check if the bypass worked or not. Header: X-Rocket-Nginx-Serving-Static set $rocket_reason ""; # Reason why cache file was not used. If cache file is used, what file was used @@ -56,7 +56,7 @@ if ($http_accept_encoding ~ gzip) { # Is Brotli accepted by client ? if ($http_accept_encoding ~ br) { - set $rocket_encryption ""; + set $rocket_encryption "#!# BROTLI_SUFFIX #!#"; } # Is HTTPS request ? @@ -75,7 +75,7 @@ if ($rocket_is_https = "1") { # Set mobile detection file path # This variable contains a file to look for. If it exists, WP Rocket is set to # generate both Desktop and Mobile cache. -set $rocket_mobile_detection "$document_root/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$http_host/$request_uri/.mobile-active"; +set $rocket_mobile_detection "$document_root/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$host/$request_uri/.mobile-active"; # Query strings to ignore #!# QUERY_STRING_IGNORE #!# @@ -112,14 +112,14 @@ set $rocket_file_start "index$rocket_mobile_prefix$rocket_https_prefix"; # Pre-process includes #!# INCLUDE_PREPROCESS #!# -set $rocket_pre_url "/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$http_host/$rocket_uri_path/$rocket_args/"; -set $rocket_pre_file "$document_root/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$http_host/$rocket_uri_path/$rocket_args/"; +set $rocket_pre_url "/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$host/$rocket_uri_path/$rocket_args/"; +set $rocket_pre_file "$document_root/#!# WP_CONTENT_URI #!#/cache/wp-rocket/$host/$rocket_uri_path/$rocket_args/"; # Standard cache file format set $rocket_url "$rocket_pre_url$rocket_file_start$rocket_dynamic.html"; set $rocket_file "$rocket_pre_file$rocket_file_start$rocket_dynamic.html"; -# Check if gzip version cached file is available +# Check if gzip/br version cached file is available if (-f "$rocket_file$rocket_encryption") { set $rocket_file "$rocket_file$rocket_encryption"; set $rocket_url "$rocket_url$rocket_encryption"; @@ -183,12 +183,13 @@ if ($rocket_bypass = 1) { # Add header to HTML cached files location ~ /#!# WP_CONTENT_URI #!#/cache/wp-rocket/.*html$ { etag on; - add_header Vary "Accept-Encoding, Cookie"; - add_header Cache-Control "#!# HTML_CACHE_CONTROL #!#"; - add_header X-Rocket-Nginx-Serving-Static $rocket_is_bypassed; - add_header X-Rocket-Nginx-Reason $rocket_reason; - add_header X-Rocket-Nginx-File $rocket_file; - add_header X-Rocket-Nginx-Device $rocket_device; + add_header "Vary" "Accept-Encoding, Cookie"; + add_header "Cache-Control" "#!# HTML_CACHE_CONTROL #!#"; + add_header "X-Rocket-Nginx-Serving-Static" "$rocket_is_bypassed"; + add_header "X-Rocket-Nginx-Reason" "$rocket_reason"; + add_header "X-Rocket-Nginx-File" "$rocket_file"; + add_header "X-Rocket-Nginx-Device" "$rocket_device"; + add_header "X-Rocket-Nginx-Precompressed" "none"; # Global includes #!# INCLUDE_GLOBAL #!# @@ -203,13 +204,36 @@ location ~ /#!# WP_CONTENT_URI #!#/cache/wp-rocket/.*_gzip$ { gzip off; types {} default_type text/html; - add_header Content-Encoding gzip; - add_header Vary "Accept-Encoding, Cookie"; - add_header Cache-Control "#!# HTML_CACHE_CONTROL #!#"; - add_header X-Rocket-Nginx-Serving-Static $rocket_is_bypassed; - add_header X-Rocket-Nginx-Reason $rocket_reason; - add_header X-Rocket-Nginx-File $rocket_file; - add_header X-Rocket-Nginx-Device $rocket_device; + add_header "Content-Encoding" "gzip"; + add_header "Vary" "Accept-Encoding, Cookie"; + add_header "Cache-Control" "#!# HTML_CACHE_CONTROL #!#"; + add_header "X-Rocket-Nginx-Serving-Static" "$rocket_is_bypassed"; + add_header "X-Rocket-Nginx-Reason" "$rocket_reason"; + add_header "X-Rocket-Nginx-File" "$rocket_file"; + add_header "X-Rocket-Nginx-Device" "$rocket_device"; + add_header "X-Rocket-Nginx-Precompressed" "gzip"; + + # Global includes + #!# INCLUDE_GLOBAL #!# + + # HTTP includes + #!# INCLUDE_HTTP #!# +} + +# Do not br cached files that are already br'd +location ~ /#!# WP_CONTENT_URI #!#/cache/wp-rocket/.*\.br$ { + etag on; + #!# BROTLI_OFF #!# + types {} + default_type text/html; + add_header "Content-Encoding" "br"; + add_header "Vary" "Accept-Encoding, Cookie"; + add_header "Cache-Control" "#!# HTML_CACHE_CONTROL #!#"; + add_header "X-Rocket-Nginx-Serving-Static" "$rocket_is_bypassed"; + add_header "X-Rocket-Nginx-Reason" "$rocket_reason"; + add_header "X-Rocket-Nginx-File" "$rocket_file"; + add_header "X-Rocket-Nginx-Device" "$rocket_device"; + add_header "X-Rocket-Nginx-Precompressed" "br"; # Global includes #!# INCLUDE_GLOBAL #!# @@ -219,10 +243,10 @@ location ~ /#!# WP_CONTENT_URI #!#/cache/wp-rocket/.*_gzip$ { } # Debug header (when file is not cached) -add_header X-Rocket-Nginx-Serving-Static $rocket_is_bypassed; -add_header X-Rocket-Nginx-Reason $rocket_reason; -add_header X-Rocket-Nginx-File $rocket_file; -add_header X-Rocket-Nginx-Device $rocket_device; +add_header "X-Rocket-Nginx-Serving-Static" "$rocket_is_bypassed"; +add_header "X-Rocket-Nginx-Reason" "$rocket_reason"; +add_header "X-Rocket-Nginx-File" "$rocket_file"; +add_header "X-Rocket-Nginx-Device" "$rocket_device"; # Global includes #!# INCLUDE_GLOBAL #!# diff --git a/rocket-parser.php b/rocket-parser.php index fafd083..c0ef33a 100644 --- a/rocket-parser.php +++ b/rocket-parser.php @@ -79,6 +79,12 @@ protected function generateConfigurationFiles($config) : void { } $output = str_replace('#!# WP_CONTENT_URI #!#', $wp_content_folder, $output); + // add_header vs. more_set_headers + if (isset($section['use_more_set_headers']) && $section['use_more_set_headers'] == '1') { + $output = preg_replace('~(\s*)add_header "([^"]+)" "([^"]+)";~m', '$1more_set_headers "$2: $3";', $output); + } + // Otherwise we can leave it as is. + // Cache Control $html_cache_control = ''; if (isset($section['html_cache_control']) && !empty($section['html_cache_control'])) { @@ -86,6 +92,16 @@ protected function generateConfigurationFiles($config) : void { } $output = str_replace('#!# HTML_CACHE_CONTROL #!#', $html_cache_control, $output); + // Brotli support + $brotli_suffix = ''; + if (isset($section['brotli_suffix']) && !empty($section['brotli_suffix'])) { + $brotli_suffix = $section['brotli_suffix']; + } + $output = str_replace('#!# BROTLI_SUFFIX #!#', $brotli_suffix, $output); + + // We should also handle the possibly unknown directives (as brotli is not in nginx core yet) + $output = str_replace('#!# BROTLI_OFF #!#', (empty($brotli_suffix) ? '' : 'brotli off;'), $output); + // Cookies $cookies = ''; if (isset($section['cookie_invalidate']) && is_array($section['cookie_invalidate'])) {