File concatenation and minification
By default, if enqueued, an application’s JavaScript and CSS files are concatenated in order to reduce the number of requests that occur on a single page load. CSS files are minified as well as concatenated, which reduces file size by removing unnecessary white space.
- Logic for concatenation is handled by the nginx-http-concat plugin.
- Concatenated assets can be identified in the “Network” tab of a browser inspector by request paths beginning with
/_static/??
. - Concatenated assets are cached by NGINX with a
Cache-Control
HTTP header set tocache-control: max-age=31536000
(1 year). This cache is busted when resource versions are updated.
The responses that are generated by VIP sites are served from a global network of edge cache server locations. This allows a majority of a site’s traffic, including concatenated scripts and styles, to be served directly from an edge location closest to a site’s visitors without ever hitting a line of PHP. This results in the low-latency and high-performance impact expected from a CDN.
Limitations
Files that are enqueued from a resource outside of an application’s wpcomvip GitHub repository will not be minified or concatenated. This includes:
- Files enqueued from an application’s
/uploads
directory (which is mapped to the external VIP File System object store). - All third-party resources.
Some third-party performance scans may not recognize VIP’s CDN as a result of concatenated files being served from the same domain as the VIP site.
Versioning to bust the cache
By default, the VIP Platform concatenates JavaScript and CSS files that are enqueued in an application’s codebase. The concatenated files are cached with a Cache-Control
HTTP header set to cache-control: max-age=31536000
(1 year).
To bust the cache on deployment, the $ver
value in the enqueue must be incrementally updated when changes are made to enqueued JavaScript and CSS files. An updated $ver
value effectively generates a new and unique static URL for the concatenated assets. The $ver
value can be updated in functions such as wp_enqueue_script()
, wp_enqueue_style()
, wp_register_script()
, and wp_register_style()
.
// When changes are made to js/plugin.js the $ver variable should be incrementally updated.
$ver = '1.1';
wp_enqueue_script( 'plugin_script', plugins_url( 'js/plugin.js', __FILE__ ), array(), $ver );
Because file Input/Output (I/O) operations can be expensive, VIP does not recommend using the function filemtime()
to populate a value for $ver
on production environments.
Script tag attributes filter
Accepted values: defer
| async
| nomodule
| crossorigin
| integrity
| type
| nonce
| referrerpolicy
Script attributes such as async
or defer
can be added to concatenated scripts with the js_concat_script_attributes
filter.
In this example, the attributes async
and defer
are added to all concatenated scripts:
add_filter( 'js_concat_script_attributes', function( $args, $href, $js_array, $jsconcat ) {
return stristr( $href, '_static' ) !== false ? [ 'async', 'defer' ] : [];
}, 10, 4 );
Enable or disable concatenation
Available filters can be used to exclude specific JavaScript or CSS files from concatenation bundles. When enabling or disabling concatenation for JavaScript or CSS, consider including is_admin()
logic to specify if the filter will be applied to the front end of a site, the WordPress Admin dashboard, or both.
For example, file concatenation in the WP Admin is enabled by default for CSS and disabled for JavaScript. This code example demonstrates how to modify these default settings and disable CSS file concatenation only in WP Admin (leaving it enabled on the front end):
// Disable CSS concatenation for WP Admin.
if ( is_admin() ) {
add_filter( 'css_do_concat', '__return_false' );
}
js_do_concat
filter
Example code to exclude specific JavaScript files from concatenation with the js_do_concat
filter:
add_filter( 'js_do_concat', 'my_vip_js_concat_filter', 10, 2 );
// Do not include my-script-handle in concatenated bundles.
function my_vip_js_concat_filter( $do_concat, $handle ) {
if ( 'my-script-handle' === $handle ) {
return false;
}
return $do_concat;
}
To disable concatenation for all JavaScript files, set the js_do_concat
filter to return __return_false
:
// Disable JS concatenation.
add_filter( 'js_do_concat', '__return_false' );
css_do_concat
filter
Example code to exclude specific CSS files from concatenation with the css_do_concat
filter:
add_filter( 'css_do_concat', 'my_vip_css_concat_filter', 10, 2 );
// Do not include my-custom-css-handle in concatenated CSS bundles.
function my_vip_css_concat_filter( $do_concat, $handle ) {
if ( 'my-custom-css-handle' === $handle ) {
return false;
}
return $do_concat;
}
To disable concatenation for all CSS files, set the css_do_concat
filter to return __return_false
:
// Disable CSS concatenation.
add_filter( 'css_do_concat', '__return_false' );
Troubleshooting
In-browser debugging methods
Query parameters can be added to a URL in the browser to disable concatenation and help determine if a JavaScript error or CSS issue is related to concatenation.
Adding these parameters to an affected URL in the browser will load the individual JavaScript or CSS files for the request instead of the static concatenated bundle. These query parameters are only meant to be used for testing in a browser and should not be added to code.
- JavaScript:
concat_js=false
- CSS:
concat_css=false
Updated concatenated assets not loading as expected
It is possible for built assets and concatenation to cause short-term brokenness in CSS and/or JavaScript when combined with full page caching.
Because previously concatenated bundles are cached at the edge, newly deployed changes will usually reference a new bundle, while existing cached pages will reference an old one. If either needs to be reconstructed due to a cache miss, the enqueued files must still be available on the server – the URL contains the filenames (usually compressed).
Because of this, avoid deleting older versions of those built files for at least 30 minutes so they can be used as needed. If 404s occur for previously-good bundles, this is a likely cause – one of the component files is missing. When overwriting these files, an old URL may suddenly contain the new assets even though the page was generated before the deploy.
Last updated: September 09, 2024