Contents
Introduction
This article is a complete, detailed tutorial on how to register custom image sizes in WordPress and how to regenerate image files for previously uploaded images using PHP. It covers where to register sizes, how WordPress creates image files, programmatic regeneration for single or multiple attachments, using the image editor, deleting old sizes, WP-CLI, server considerations, retina/2x strategies, and common troubleshooting. Code examples are provided for every important step — each example is placed inside the required
tag or appropriate language tag.Why register custom image sizes?
WordPress generates a series of intermediate image sizes when an image is uploaded. Registering custom sizes with add_image_size lets you:
- Serve appropriately sized images for different layout contexts (hero, thumbnail, gallery, etc.).
- Improve page load performance by avoiding unnecessarily large images.
- Leverage WordPress core responsive images (srcset) by referencing registered sizes.
Where to register image sizes
Add image size registration to your theme or plugin and hook it to after_setup_theme (for themes) or init (in some plugin scenarios). Use functions.php for themes. Always register sizes before attempting to regenerate images so the regeneration code sees the new sizes.
Basic examples: register sizes and set post-thumbnail
lt?php // In functions.php or a plugin file. Hook into after_setup_theme. add_action( after_setup_theme, mytheme_register_image_sizes ) function mytheme_register_image_sizes() { // Set default post-thumbnail size set_post_thumbnail_size( 800, 450, true ) // width, height, crop // Add custom sizes add_image_size( hero, 1600, 700, true ) // Hard-cropped (center) add_image_size( homepage-thumb, 400, 250, true ) // Hard-cropped add_image_size( content-wide, 1200, 0, false ) // Constrain to width only, proportional height add_image_size( square-small, 300, 300, array( center, center ) ) // Explicit crop position } ?gt
Notes on the crop parameter
- crop = false (default): proportional resize, no crop.
- crop = true: hard crop to exact dimensions (center crop by default).
- crop = array( x_crop, y_crop ): specify crop origin positions, e.g. array(left,top) or array(center,center).
How WordPress generates image files
On upload, WordPress uses wp_get_image_editor to create intermediate sizes. The list of sizes used is derived from get_intermediate_image_sizes() which returns registered sizes. The creation and metadata generation are handled by wp_generate_attachment_metadata which in turn calls image editor methods and image_make_intermediate_size.
Get a list of all registered sizes and their dimensions
To inspect registered sizes (useful before regenerating), use this helper that accounts for core sizes and sizes added with add_image_size:
lt?php function mytheme_get_all_image_sizes() { global _wp_additional_image_sizes sizes = array() foreach ( get_intermediate_image_sizes() as s ) { if ( in_array( s, array( thumbnail, medium, large ), true ) ) { sizes[ s ] = array( width => get_option( {s}_size_w ), height => get_option( {s}_size_h ), crop => (bool) get_option( {s}_crop ), ) } elseif ( isset( _wp_additional_image_sizes[ s ] ) ) { sizes[ s ] = _wp_additional_image_sizes[ s ] } } return sizes } ?gt
Programmatically regenerating image sizes for a single attachment
The simplest and recommended method is to use wp_generate_attachment_metadata which will re-create intermediate sizes based on currently registered image sizes and return a metadata array that you can store with wp_update_attachment_metadata.
Regenerate sizes for one attachment (safe minimal)
lt?php function mytheme_regenerate_attachment_sizes( attachment_id ) { // Get full path to original uploaded file fullsizepath = get_attached_file( attachment_id ) if ( ! file_exists( fullsizepath ) ) { return new WP_Error( file_missing, File for attachment does not exist. ) } // Generate metadata (this creates sizes according to currently registered sizes) metadata = wp_generate_attachment_metadata( attachment_id, fullsizepath ) if ( is_wp_error( metadata ) ) { return metadata } // Update metadata in the database wp_update_attachment_metadata( attachment_id, metadata ) return metadata } ?gt
Delete old intermediate files before regenerating (optional)
If you want to remove previously generated image files for an attachment before creating new ones, read the attachment metadata, unlink the intermediate files, then regenerate. Be careful: deleting files is irreversible — ensure correct path and permissions.
lt?php function mytheme_delete_intermediate_sizes( attachment_id ) { metadata = wp_get_attachment_metadata( attachment_id ) if ( empty( metadata ) empty( metadata[file] ) ) { return } upload_dir = wp_upload_dir() dir = pathinfo( metadata[file], PATHINFO_DIRNAME ) // e.g. 2025/09 basedir = trailingslashit( upload_dir[basedir] ) . dir . / if ( ! empty( metadata[sizes] ) is_array( metadata[sizes] ) ) { foreach ( metadata[sizes] as size => info ) { file = basedir . info[file] if ( file_exists( file ) ) { @unlink( file ) } } } } function mytheme_regenerate_attachment_sizes_replace( attachment_id ) { // Delete old sizes mytheme_delete_intermediate_sizes( attachment_id ) // Regenerate and update metadata return mytheme_regenerate_attachment_sizes( attachment_id ) } ?gt
Regenerating all images: batch and performance considerations
For sites with many images, do not try to regenerate all attachments in a single request. Use one of these approaches:
- Use WP-CLI (recommended for large sites): runs in CLI environment with higher limits.
- Process in chunks (batches) with pagination and cron or background jobs (Action Scheduler or custom background process).
- Trigger per-image regeneration on demand (e.g., when an image is viewed or re-saved).
Example: simple batch regeneration (admin-only, limited memory/time)
lt?php // WARNING: This simple example is for small sets or testing only. // For large sites use WP-CLI or background processing. add_action( admin_post_mytheme_regenerate_all_images, mytheme_handle_regenerate_all_images ) function mytheme_handle_regenerate_all_images() { if ( ! current_user_can( manage_options ) ) { wp_die( Unauthorized ) } // Query attachments in batches paged = 1 posts_per_page = 50 do { query = new WP_Query( array( post_type => attachment, post_mime_type => image, posts_per_page => posts_per_page, paged => paged, fields => ids, ) ) if ( empty( query->posts ) ) { break } foreach ( query->posts as attachment_id ) { // Optional: delete old sizes first // mytheme_delete_intermediate_sizes( attachment_id ) mytheme_regenerate_attachment_sizes( attachment_id ) } paged // Optional: sleep to reduce server load sleep(1) } while ( query->found_posts > ( paged - 1 ) posts_per_page ) wp_redirect( admin_url() ) // send back to admin exit } ?gt
Using WP-CLI
WP-CLI is the most reliable way to regenerate all images because it runs outside PHP-FPM/Apache limits and can be resumed or batched easily.
# Regenerate thumbnails for all images (interactive) wp media regenerate # Regenerate for some images (with ID list) wp media regenerate 123 456 789 # Force overwrite and skip confirmation wp media regenerate --yes --skip-delete
See the built-in WP-CLI help for additional flags. WP-CLI calls the same underlying functions so it respects registered sizes if your theme/plugin code has already registered them before the command runs.
Advanced: using the image editor directly
For custom workflows where you need to control filenames or create images in non-standard ways, use wp_get_image_editor and its methods. This is more low-level than wp_generate_attachment_metadata.
lt?php fullsizepath = get_attached_file( attachment_id ) editor = wp_get_image_editor( fullsizepath ) if ( is_wp_error( editor ) ) { // handle error } else { // Example: create one custom size dest_file = editor->generate_filename( custom-800x ) editor->resize( 800, 0 ) // width only, proportional height saved = editor->save( dest_file ) if ( ! is_wp_error( saved ) ) { // saved contains path, width, height, file keys } } ?gt
Retina / 2x approach
WordPress core adds responsive srcset and sizes attributes automatically when you use the_post_thumbnail or wp_get_attachment_image. To serve high-DPI images you can:
- Rely on srcset and let the browser pick the correct size for device pixel ratio.
- Register larger sizes (e.g., twice the width of your layout) so srcset includes higher-resolution candidates (e.g., register both hero and hero@2x).
- Use a plugin that generates @2x files and provides the 2x file URLs in srcset if you need explicit naming.
Saving and using sizes in templates
Use the registered size names in template functions:
lt?php // Echo an img tag for a specific image size echo wp_get_attachment_image( attachment_id, hero ) // Get URL and attributes src = wp_get_attachment_image_src( attachment_id, hero ) if ( src ) { // src[0] = URL, src[1] = width, src[2] = height echo ltimg src= . esc_url( src[0] ) . width= . intval( src[1] ) . height= . intval( src[2] ) . alt= /> } ?gt
Deleting old sizes vs leaving them
WordPress never deletes generated intermediate files automatically when you change registered sizes. When you regenerate, you may accumulate old files. Decide on a strategy:
- Leave old files (small and safe, but disk accumulates).
- Delete files before regeneration (requires careful metadata-based unlinking).
- Use established plugins like Force Regenerate Thumbnails which provide deletion regeneration with safety checks.
Multisite considerations
On multisite, upload directories differ by site. Use get_attached_file and wp_upload_dir to compute paths. Run regeneration per-site or via network admin using WP-CLI for the whole network.
Common pitfalls and troubleshooting
- Sizes not created: Ensure add_image_size runs early (after_setup_theme). If you regenerate before sizes are registered, the new sizes wont be made.
- Permissions: File creation requires write permission to wp-content/uploads. Check ownership and permission bits.
- Memory/timeout: Large images may exhaust memory or max_execution_time. Use WP-CLI or batch processing and consider increasing limits temporarily.
- Imagick vs GD: Server image library differences can affect quality and available operations. If an image editor returns WP_Error, inspect for underlying library problems.
- Old files persist: Delete them if desired, but ensure metadata and URLs are updated properly.
- Wrong crop focus: Use manual cropping tools or specify crop arrays for add_image_size, or use image editors to set custom crop coordinates.
- Responsive images not showing up: Ensure your theme outputs images using the_post_thumbnail or wp_get_attachment_image so core srcset logic runs.
Security and capability checks
When exposing regeneration to user actions (admin UI or AJAX), enforce capability checks (current_user_can) and nonces. Only allow trusted users to run mass regeneration.
Quick checklist before regenerating
- Register new sizes in functions.php or plugin code and confirm they are present (use get_intermediate_image_sizes or the helper above).
- Backup your uploads directory (recommended for safety if you plan to delete files).
- Decide whether to delete old sizes or leave them.
- Choose regeneration method (WP-CLI, batch PHP, plugin, or single-attachment regeneration).
- Test on a small set first.
- Monitor for errors, memory, and disk usage during the process.
Useful links
- add_image_size() reference
- wp_generate_attachment_metadata() reference
- wp_get_image_editor() reference
- get_intermediate_image_sizes() reference
- WP-CLI media regenerate
Complete end-to-end example
This example registers sizes and exposes a safe admin action that regenerates all images in small batches. It demonstrates proper hooks, capability checks, and batch processing (suitable for small-to-medium sites — for very large sites prefer WP-CLI).
lt?php // Register sizes add_action( after_setup_theme, function() { add_image_size( hero, 1600, 700, true ) add_image_size( homepage-thumb, 400, 250, true ) add_image_size( content-wide, 1200, 0, false ) } ) // Admin action to regenerate in batches (trigger via admin link or form) add_action( admin_post_mytheme_regenerate_all_images, function() { if ( ! current_user_can( manage_options ) ) { wp_die( Unauthorized ) } // Simple nonce check if form used if ( isset( _REQUEST[_wpnonce] ) ! wp_verify_nonce( _REQUEST[_wpnonce], mytheme_regenerate ) ) { wp_die( Nonce check failed ) } paged = isset( _GET[paged] ) ? max( 1, intval( _GET[paged] ) ) : 1 per_page = 30 query = new WP_Query( array( post_type => attachment, post_mime_type => image, posts_per_page => per_page, paged => paged, fields => ids, ) ) if ( empty( query->posts ) ) { wp_redirect( admin_url() ) exit } foreach ( query->posts as attachment_id ) { // Optional: delete old intermediate sizes // mytheme_delete_intermediate_sizes( attachment_id ) // Regenerate res = mytheme_regenerate_attachment_sizes( attachment_id ) // Optionally log errors if ( is_wp_error( res ) ) { error_log( Regeneration failed for . attachment_id . : . res->get_error_message() ) } } // Redirect to next page if there are more next_page = paged 1 redirect = add_query_arg( array( action => mytheme_regenerate_all_images, paged => next_page, _wpnonce => wp_create_nonce( mytheme_regenerate ), ), admin_url( admin-post.php ) ) // Small sleep to reduce server load sleep(1) wp_redirect( redirect ) exit } ) function mytheme_regenerate_attachment_sizes( attachment_id ) { fullsizepath = get_attached_file( attachment_id ) if ( ! fullsizepath ! file_exists( fullsizepath ) ) { return new WP_Error( missing_file, Original file not found. ) } metadata = wp_generate_attachment_metadata( attachment_id, fullsizepath ) if ( is_wp_error( metadata ) ) { return metadata } wp_update_attachment_metadata( attachment_id, metadata ) return metadata } ?gt
Follow the checklist, test on a staging environment, and prefer WP-CLI for large sites. The code examples above illustrate all the typical and advanced needs for registering and regenerating image sizes in WordPress with PHP.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |