Purpose
The image localizer automatically downloads external images referenced in ACF block fields and stores them locally. This improves privacy (no third-party image requests from visitors), reliability (images survive if the external source goes down), and performance (images are served from your own domain or CDN).
How It Works
The localizer hooks into wp_insert_post_data, which fires before the post is saved to the database. This means URL rewriting happens in a single pass without needing a recursive wp_update_post call.
The process:
- Check if the post content contains any ACF blocks (
wp:acf/in the content). - Parse the block tree from the content.
- Walk all ACF blocks recursively (including inner blocks).
- For each block’s
attrs.datavalues, scan for image URLs matching common extensions (jpg, jpeg, png, gif, webp, avif, bmp, svg). - For each external URL found, download the image and replace the URL with the local path.
- If any URLs were replaced, re-serialize the blocks back into post content.
A re-entrance guard prevents infinite loops if the filter triggers another save.
Local URL Detection
The function acf_blocks_is_local_url() determines whether an image URL is already local. The following are treated as local:
- Exact host match – The URL’s hostname matches the site’s hostname (with or without
www.). - Subdomain match – The URL’s hostname ends with
.{site-domain}(e.g.cdn.example.comwhen your site isexample.com). - Bunny CDN – Any URL on
*.b-cdn.netis treated as local, since this is a common CDN provider. - Custom hosts – Additional hosts can be declared local via the
acf_blocks_is_local_image_urlfilter.
Storage Location
Downloaded images are saved to wp-content/uploads/acf-blocks-plugin/images/. The directory is created on first use with two security measures:
- An
.htaccessfile that disables directory listing and blocks PHP execution. - A blank
index.phpfile as a fallback directory listing protection.
Filename Strategy
Each downloaded image is named using an MD5 hash of the source URL plus the original file extension: {md5(url)}.{ext}. This provides:
- Idempotency – The same URL always produces the same filename, so images are never re-downloaded.
- No collisions – Different URLs produce different filenames even if the original filenames were identical.
If the file extension cannot be determined from the URL, it defaults to jpg. Only allowed extensions are accepted: jpg, jpeg, png, gif, webp, avif, svg, bmp.
Image Validation
After downloading, the image is validated:
wp_get_image_mime()checks that the file is a recognized image type.- For SVG files (which
wp_get_image_mime()does not recognize),mime_content_type()is used as a fallback. - If validation fails, the temporary file is deleted and the original URL is kept.
The acf_blocks_is_local_image_url Filter
This filter lets you declare additional hostnames as “local” so their images are not downloaded:
add_filter( 'acf_blocks_is_local_image_url', function( $is_local, $url_host, $site_bare ) {
if ( $url_host === 'my-cdn.example.com' ) {
return true;
}
return $is_local;
}, 10, 3 );
Parameters:
$is_local(bool) – Whether the URL is currently considered local (alwaysfalsewhen this filter runs, since built-in checks already passed).$url_host(string) – The full hostname of the image URL.$site_bare(string) – Your site’s hostname withwww.stripped.