Problem/Motivation

Drupal supports compressing aggregated assets with gzip.
It would be nice to also support brotli, since it provides better performances.

advagg provides this functionality but only on non-aggregated files.

Proposed resolution

Modify AssetDumper to generate Brotli-compressed assets alongside gzipped files and update the .htaccess file to serve them.

Rename the system.performance.css.gzip and system.performance.js.gzip settings to system.performance.css.compress and system.performance.js.compress. These boolean settings would enable every available compression algorithm (currently gzip and Brotli but more could be added in the future).

Remaining tasks

review, commit

User interface changes

no

Introduced terminology

no

API changes

config renamed from system.performance.*.gzip to system.performance.*.compress

Data model changes

no

Release notes snippet

Drupal now generates Brotli-compressed versions of aggregated CSS and JS assets when the brotli PHP extension is installed.

Issue fork drupal-3184242

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

prudloff created an issue. See original summary.

cilefen’s picture

Version: 9.0.x-dev » 9.2.x-dev
prudloff’s picture

StatusFileSize
new4.17 KB

The attached patch add new system.performance.css.brotli and system.performance.js.brotli settings that enable Brotli compression.

I could not find a GUI to enable/disable gzip compression (it seems it is only provided by advagg?) so I did not create one for Brotli.

cilefen’s picture

Status: Active » Needs review
prudloff’s picture

Status: Needs review » Active
StatusFileSize
new4.44 KB

I just noticed that the previous patch was giving priority to gzip in .htaccess.
I put brotli first so gzip is used as a fallback if brotli is not available.

cilefen’s picture

Status: Active » Needs review

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

webflo’s picture

StatusFileSize
new6.56 KB

Re-rolled the patch from @prudloff because this patch was from the substree-split (drupal/core).

webflo’s picture

StatusFileSize
new4.52 KB

And the same patch without patches to /.htaccess - the patch can not be applied with composer otherwise.

Status: Needs review » Needs work

The last submitted patch, 9: 3184242-9.patch, failed testing. View results

yogeshmpawar’s picture

Status: Needs work » Needs review
StatusFileSize
new8.59 KB
new2.03 KB

Updated patch will fix test failures.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

Adding #1040534: Rewrite rules for gzipped CSS and JavaScript aggregates cause lots of lstats for files that will never exist as a related issue.

One issue here is that if brotli compression isn't available on the server, this will add even more lstats for files that will never exist. Trying to think through ways around this and how to reconcile it with the latest approach on that issue.

socialnicheguru’s picture

Does this just work with apache2?
Does it work with nginx?

catch’s picture

#1040534: Rewrite rules for gzipped CSS and JavaScript aggregates cause lots of lstats for files that will never exist improves the regexp so that it doesn't look for files like .gz.gz, but that won't help on servers that don't have brotli compression enabled - where we'd be looking for a .br file that will never exist.

However one of the possible solutions in #1040534: Rewrite rules for gzipped CSS and JavaScript aggregates cause lots of lstats for files that will never exist was to write a .htaccess file to the aggregate folder with the rewrite rules, so that it only applies to those folders.

If we were to do that, we could also potentially write different .htaccess files dependent on whether brotli compression is available or not, then sites without it won't check file existence for .br files.

socialnicheguru’s picture

How would that work if you are using nginx server as they do not use .htaccess?

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

gaëlg’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
ravi.shankar’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new8.66 KB
new5.24 KB

Added reroll of patch #12 on Drupal 10.1.x.

jnoordsij’s picture

How would that work if you are using nginx server as they do not use .htaccess?

For nginx you would need to install the brotli module (see https://github.com/google/ngx_brotli), then add a `brotli_static on` directive to the correct block of your configuration.

prudloff’s picture

StatusFileSize
new6.6 KB

Here is #20 without changes to .htaccess so that it can be applied with Composer.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new3.4 KB

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

jnoordsij’s picture

Status: Needs work » Needs review
StatusFileSize
new8.74 KB
new6.64 KB

Added rerolls of patch #20 and the non-.htaccess version of #22 on Drupal 10.1.x.

I also added some changes to the added .htaccess rules to make the rewriterule conditions consistent with the ones for gzip.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative, +Needs tests, +Needs upgrade path, +Needs change record

So taking a look at this

Could we add it's own test case? Seems some tests had to be updated for it but not sure if it's actually testing.

Since the schema is updating most likely will need an upgrade path + upgrade path tests.

If this new setting is required (which seems to be) it will require a change record also

Thanks!

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

What if new compression algo will be added to browsers?

Better to deprecate "gzip" option in favor of "compress" which will be common trigger to produce all possible assets archives.
Also it could be a sequence of algo to limit :

compress:
- gzip
- brotli
+++ b/core/lib/Drupal/Core/Asset/AssetDumper.php
@@ -77,6 +77,16 @@ public function dumpToUri(string $data, string $file_extension, string $uri): st
+    if (extension_loaded('brotli') && \Drupal::config('system.performance')->get($file_extension . '.brotli')) {

+++ b/core/modules/system/config/install/system.performance.yml
@@ -4,6 +4,7 @@ cache:
 css:
   preprocess: true
   gzip: true
+  brotli: false

@@ -12,4 +13,5 @@ fast_404:
 js:
   preprocess: true
   gzip: true
+  brotli: false

Not sure it makes sense to introduce new configuration and without UI, it could re-use "gzip" setting to produce compressed assets using gzip/brotli

catch’s picture

A compress item with a sequence sounds good.

Having said that we've got the problem that the .htaccess rules can't be configured - they will always need to check in order of likelihood-of-support first. So if we add brotli support to htaccess but it's disabled by the site, the rewrite rules will still look for those files.

Makes me wonder if we should just do 'compress' as a boolean and write out everything we possibly can.

jnoordsij’s picture

Added rerolls of patches in #24 on Drupal 10.2.x. I think they should also apply on 11.x-dev.

jnoordsij’s picture

Added rerolls of patches in #29 on Drupal 10.2.x.

jnoordsij’s picture

socialnicheguru’s picture

Is there a variable we can set in settings.php?

andypost’s picture

Not sure how tests can cover it but CR should mention update of nginx conf as well

pgndrupal’s picture

with a new D11 v11.1.0 install,

i see in generated assets,

ls -1 web/sites/default/files/css/ | sort | head -n 6
	css_1A5CXfNop7cNp-NPI4DF_OGMQem-KN6lxj_k9OzGOpU.css
	css_1A5CXfNop7cNp-NPI4DF_OGMQem-KN6lxj_k9OzGOpU.css.gz
	css_1ecH70dXxW6SrorXnALqr-8DiJZhZ4g3Y6cBbPZOuEc.css
	css_1ecH70dXxW6SrorXnALqr-8DiJZhZ4g3Y6cBbPZOuEc.css.gz
	css_1TuJBQ_3nvA_oZ-1Bd-SoJw7dHab08LO29K2pikMvXw.css
	css_1TuJBQ_3nvA_oZ-1Bd-SoJw7dHab08LO29K2pikMvXw.css.gz
	...

, no brotli (.br)

checking,

cat ./web/core/modules/system/config/install/system.performance.yml (yaml)                                                                                                                   cache:
	page:
		max_age: 0
	css:
	preprocess: true
	gzip: true
	fast_404:
	enabled: true
	paths: '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i'
	exclude_paths: '/\/(?:styles|imagecache)\//'
	html: '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'
	js:
	preprocess: true
	gzip: true

status here says "Needs work", and "Remaining tasks: I will submit a patch."

the MR, #9980 submitted a month ago, is "Ready to merge" and "1 commit will be added to 11.x".
but has not been merged.

@prudloff

what's outstanding?
what release can this be expected in?

sd123’s picture

Hope this will be added soon...

It would be great if automatic creation of static gzip and brotli files is also added for all svg files people have in their themes and uploaded files directories. I am now creating those manually on my server.

prudloff’s picture

I added a test but it requires the brotli PHP extension.
I managed to build it in the CI but it does not seem to be enabled in tests so I'm not sure that's the correct way to do this.
Should we create an issue in the drupalci project to get the extension included in the Docker image?

Makes me wonder if we should just do 'compress' as a boolean and write out everything we possibly can.

We might want to add support for zstandard compression after this (#3346184: Does Drupal support ZSTD compression?) so I agree it might be easier to keep a single boolean that enables every possible algorithm depending on what the server can generate.
I'm not sure there is a use case for enabling gzip but not brotli.

andypost’s picture

as both kinds of compression are common I think better to add them to images, just file issue to https://www.drupal.org/project/issues/drupalci_environments

prudloff’s picture

Status: Needs work » Postponed
Related issues: +#3512083: Add brotli, igbinary, zstd PHP extensions
andypost’s picture

Added both brotli and zstd to dev images, rebased and looks like tests passed

PS: probably zstd could be added in related

prudloff’s picture

This issue is already quite old so we might want to keep the scope small in order to get it committed then add zstd in a followup.

andypost’s picture

Sure, let's focus the issue on title and there's so many "needs" tags so not sure it fits into 11.2

andypost’s picture

CI images now has brotli 0.17.0 with latest fixes https://github.com/kjdev/php-ext-brotli/commit/48c719a9f0e2b1ae53e08acf2...

andypost’s picture

Issue tags: -Needs tests

tests are here

prudloff’s picture

Issue tags: -Needs upgrade path

I updated the MR to use a single setting for both gzip and brotli (and future algorithms).
I also added an upgrade path.

We still need a change record I think.

andypost’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record

Created CR using Claude Opus 4

prudloff’s picture

Status: Needs review » Needs work

The CR seems to contain some hallucinations:

  • The name of the new key is compress, not compression.
  • It talks about a setting on /admin/config/development/performance but I see no such setting.
  • The name of the update hook is incorrect.
andypost’s picture

Status: Needs work » Needs review

Fixed https://www.drupal.org/node/3526344/revisions/view/14005103/14005483

It talks about a setting on /admin/config/development/performance but I see no such setting.

this is where aggregate js/css checkboxes are

prudloff’s picture

this is where aggregate js/css checkboxes are

The aggregate setting is not the same as the gzip/compress setting (which as far as I can tell does not have a UI).

andypost’s picture

not the same

yes, this settings enabling aggregation, now having aggregated assets core applies compression

sd123’s picture

What about svg and webmanifest assets? Or are non-aggregated assets beyond the scope of this issue report?

prudloff’s picture

@sd123 this is out of the scope of this issue. Feel free to open a separate issue about this (if there isn't one already).

sd123’s picture

Ok, maybe a suggestion for the nginx example config in the CR:

Isn't it better and cleaner to combine both as follows or can this induce problems?

location ~ ^/sites/.*/files/(css|js)/(.*)\.(css|js)$ {
    gzip_static on;
    brotli_static on;
    try_files $uri.br $uri.gz $uri =404;
}

Not sure if this should be in the example configuration, but it also can be with better caching parameters:

location ~ ^/sites/.*/files/(css|js)/(.*)\.(css|js)$ {
    gzip_static on;
    brotli_static on;
    expires max;
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri.br $uri.gz $uri =404;
}
smustgrave’s picture

Shouldn't these keys be deprecated?

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

prudloff’s picture

Status: Needs work » Needs review

I merged the latest 11.x and deprecated the gzip properties instead of removing them.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

prudloff’s picture

Status: Needs work » Needs review

I merged the latest 11.x.

dcam’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

I left some comments on the MR.

The issue summary needs to be updated. The full summary template should be applied. The proposed resolution section needs to be updated with information about the changes to the system.performance config schema. And the UI Changes section still mentions modifying the Performance form, which is no longer happening.

prudloff’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update

I applied the suggested changes.

dcam’s picture

Status: Needs review » Reviewed & tested by the community

My feedback was addressed.

catch’s picture

Status: Reviewed & tested by the community » Needs review

One question on the MR.

dcam’s picture

Status: Needs review » Needs work

Setting to Needs Work to fix the line flagged by @catch.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

prudloff’s picture

Status: Needs work » Needs review

I answered the comment.

godotislate’s picture

Deprecation version should be bumped before being committed. I also removed the line in the CR about migrating settings from "admin/config/development/performance", since the form does not control compression settings, and the only way to change them is programmatically or through CLI.

dcam’s picture

Status: Needs review » Needs work

Needs Work for another rebase and for the latest comments made by @godotislate.

prudloff’s picture

Status: Needs work » Needs review

I rebased the MR and applied suggestions.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new97 bytes

The Needs Review Queue Bot tested this issue. The merge request has merge conflicts and cannot be merged. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

prudloff’s picture

Status: Needs work » Needs review
dcam’s picture

Status: Needs review » Reviewed & tested by the community

Recent feedback from @godotislate was addressed. I'm going to go ahead and RTBC this, but I realized upon re-review that the removal version is still set as D12. That may need to be updated to D13. I'm not 100% sure because it's been a while since we had those discussions.

catch’s picture

Went back and forth on the config schema deprecation a bit (with myself...).

Assuming we're able to land this in both 12.x and 11.4, then the update will be in both versions, so whether you go 11.3 -> 11.4 -> 12.0 or 11.3 -> 12.0 the update will run and your config will be correct.

This isn't a config entity so we don't need to worry about shipped configuration with modules, the only case where it could be shipped would be in a recipe (unlikely) or distribution (more likely if they ship essentially the entire contents of the config/sync directory).

I'm not entirely sure about the possibly disruption to distributions after typing that out, it might be easier to defer the deprecation for removal in 13.0 which would be the same version that the update path is removed in though.

We need a release note here because this changes site-owner managed files.

Also tagging for 11.4.0 release highlights since it'll be another thing to put in the performance section.

catch’s picture

Status: Reviewed & tested by the community » Needs work
andypost’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: +DevDaysAthens2026

added snippet and IS a bit

andypost’s picture

Issue summary: View changes
andypost’s picture

Issue tags: -Needs release note

added release note snippet

dcam’s picture

Status: Needs review » Reviewed & tested by the community

The added release note is a good one-sentence summary of the change.

  • catch committed 107e1715 on 11.x
    perf: #3184242 Support brotli compression of assets
    
    By: prudloff
    By:...

  • catch committed d8fb0e09 on main
    perf: #3184242 Support brotli compression of assets
    
    By: prudloff
    By:...
catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

Thought about the deprecation more and the use case for customising this in a distribution is so narrow I think we're fine to deprecate for removal in 12.x

Committed/pushed to main and 11.x, thanks! Manually resolved a straightforward merge conflict in 11.x for the post update, just context.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • alexpott committed 1f556401 on main
    perf: follow-up #3184242 Support brotli compression of assets
    
    By:...

godotislate’s picture

Status: Fixed » Needs work

Think this has broken HEAD. Opened https://git.drupalcode.org/project/drupal/-/merge_requests/15602 to address.

godotislate’s picture

Status: Needs work » Fixed

Oh never mind, @alexpott already got it.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

longwave’s picture

This also broke HEAD in 11.x.

Drupal\Tests\system\Kernel\Migrate\d7\MigrateSystemConfigurationTest::testConfigurationMigration
system.performance matches expected values.
Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
     ],
     'css' => Array &3 [
         'preprocess' => true,
-        'gzip' => true,
+        'compress' => true,
     ],
     'fast_404' => Array &4 [
         'enabled' => true,
@@ @@
     ],
     'js' => Array &5 [
         'preprocess' => false,
-        'gzip' => true,
+        'compress' => true,
     ],
 ]
longwave’s picture

Hotfixed in 468b307.

  • longwave committed 468b3079 on 11.x
    perf: follow-up #3184242 Support brotli compression of assets
    
    By:...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.