This project is not covered by Drupal’s security advisory policy.

S3FS Aggregation stores Drupal's aggregated CSS/JS files in Amazon S3 through the S3 File System module, and serves them straight from the s3fs host or CDN. It is meant for sites whose web servers have a read-only filesystem, such as containers on Kubernetes or ECS.

Why

Since Drupal 10.1, aggregated CSS/JS lives in the assets:// stream wrapper, a local directory. Each file is built when a browser first requests it. That needs a writable local disk on every web server, and s3fs does not take over assets:// (#3404738).

How it works

When s3fs takes over public:// ($settings['s3fs.use_s3_for_public']), the module swaps in:

  • An assets:// stream wrapper that maps assets://css/css_HASH.css to public://aggregation/RELEASE/css/css_HASH.css. Aggregates are stored and served like any other s3fs public file.
  • CSS/JS collection optimizers that write aggregates while the page renders. Core only prints aggregate URLs and builds a file when a request for it reaches Drupal. Requests to S3 or a CDN never do. A cache rebuild no longer deletes aggregates, so pages cached by a CDN keep working.
  • A CSS optimizer that writes absolute url() references, because the CSS is served from a different host than the images it points to.

Every release gets its own folder. Core names aggregates after library versions, not file contents, so a shared folder would let old and new web servers overwrite each other's files during a rolling deployment.

The module has no dependency on s3fs in its info file. Without s3fs, or with s3fs not taking over public:// (for example on a local environment), it changes nothing. With CSS/JS aggregation turned off, it has no effect either.

Difference from S3 File System for assets

S3 File System for assets also replaces the assets:// stream wrapper, but keeps aggregate URLs on Drupal's asset route. PHP then reads every CSS/JS file from S3 on every request, and core marks those responses private, no-store, so a CDN does not cache them.

S3 File System for assets S3FS Aggregation
Aggregate URLs Drupal's asset route s3fs host / CDN
Who serves a file PHP, reading it from S3 S3 / CDN, no PHP
When a file is built When requested While the page renders
Storage css/, js/ in the bucket root public://aggregation/RELEASE/
Rolling deployments Releases share file names One folder per release
Cache rebuild Deletes aggregates Keeps aggregates
CSS url() Relative Absolute
Without s3fs Requires s3fs Does nothing, safe to keep enabled

Requirements

  • Drupal 10.3, 11 or 12 (Drupal 12 as soon as S3 File System supports it)
  • S3 File System 3.x with $settings['s3fs.use_s3_for_public'] = TRUE;
  • CSS and/or JS aggregation enabled

Configuration

There is no configuration UI. Optional settings in settings.php:

// Folder inside public:// for aggregates. Default: 'aggregation'.
$settings['s3fs_aggregation.folder'] = 'aggregation';

// Release folder. Default: $settings['deployment_identifier'], or 'default'.
// Must change on every deployment that changes CSS/JS.
$settings['s3fs_aggregation.release'] = getenv('APP_RELEASE');

// Scheme and host prepended to url() references in aggregated CSS.
// Default: the host of the request that builds the aggregate.
$settings['s3fs_aggregation.css_url_base'] = 'https://www.example.com';

Old release folders are never deleted by the module. Expire them with an S3 lifecycle rule that is longer than your page cache lifetime.

Similar projects

Project information

Releases