Skip to content

Instantly share code, notes, and snippets.

@grayayer
Created February 24, 2026 00:33
Show Gist options
  • Select an option

  • Save grayayer/5e8799265d12ceaf91721ce4cae1b89d to your computer and use it in GitHub Desktop.

Select an option

Save grayayer/5e8799265d12ceaf91721ce4cae1b89d to your computer and use it in GitHub Desktop.

WordPress Image Resize Script

A WordPress utility script that automatically converts full-resolution images in Gutenberg blocks to optimized sizes (medium or large).

What It Does

This script scans all posts in your WordPress site and finds Gutenberg image blocks that are using full-resolution images. It then converts them to use a specified size (medium or large), updating both the image URL and block attributes.

When To Use This

  • Performance optimization: Full-resolution images can significantly slow down page load times
  • Bandwidth reduction: Serving appropriately-sized images reduces data transfer
  • After bulk imports: When content has been imported with full-size images
  • Site audit cleanup: When you discover posts are serving unnecessarily large images
  • Before CDN migration: Clean up image sizes before implementing a CDN strategy

Requirements

  • WordPress installation with wp-load.php accessible
  • PHP CLI access
  • Gutenberg/Block editor content (won't affect Classic Editor content)

Installation

  1. Download the script to your WordPress root directory (where wp-load.php is located)
  2. Ensure it's executable: chmod +x resize-post-images.php

Usage

Dry Run (Preview Changes)

Always run in dry-run mode first to see what will be changed:

# Preview with large size (default)
php resize-post-images.php

# Preview with medium size
php resize-post-images.php --size=medium

Execute Changes

Once you've reviewed the dry-run output:

# Execute with large size
php resize-post-images.php --execute

# Execute with medium size
php resize-post-images.php --size=medium --execute

Options

  • --size=medium or --size=large - Target image size (default: large)
  • --execute - Apply changes (without this flag, runs in dry-run mode)

What Gets Changed

The script modifies Gutenberg image blocks by:

  1. Adding or updating the sizeSlug attribute to the target size
  2. Updating the <figure> class to include size-{target}
  3. Replacing the image src URL with the target size URL

Before

<!-- wp:image {"id":123} -->
<figure class="wp-block-image">
  <img src="full-size-image.jpg" />
</figure>
<!-- /wp:image -->

After

<!-- wp:image {"id":123,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large">
  <img src="large-size-image.jpg" />
</figure>
<!-- /wp:image -->

Safety Features

  • Dry-run by default: Won't make changes unless --execute is specified
  • Smart detection: Only processes images that are currently set to full size or have no size designation
  • Size validation: Skips images where the target size doesn't exist or is the same as full size
  • Preserves small images: If an image is smaller than the target size threshold, it's left unchanged

Output

The script provides detailed output including:

  • Total posts processed
  • Posts modified
  • Images resized per post
  • Before/after dimensions for each image
  • Summary statistics

Backup Recommendation

Always backup your database before running with --execute:

# Using WP-CLI
wp db export backup-before-resize.sql

# Or use your hosting provider's backup tools

WordPress Image Sizes

WordPress default sizes:

  • Thumbnail: 150x150px (cropped)
  • Medium: 768px max width/height
  • Large: 1024px max width/height
  • Full: Original uploaded size

These dimensions can be customized in Settings → Media.

Limitations

  • Only works with Gutenberg/Block editor content
  • Requires the target image size to exist (WordPress generates these on upload)
  • Won't regenerate thumbnails if they're missing (use a plugin like Regenerate Thumbnails first)

Troubleshooting

"Target size doesn't exist": Run a thumbnail regeneration plugin to create missing image sizes.

"No changes detected": Your images might already be using appropriate sizes, or they're smaller than the target size threshold.

Memory issues with large sites: Modify the script to process posts in batches by adjusting the posts_per_page parameter.

License

MIT License - Feel free to use and modify as needed.

Contributing

Found a bug or have a suggestion? Open an issue or submit a pull request.

<?php
/**
* Resize Post Images Script
*
* This script finds all Gutenberg image blocks in post content that are using
* full resolution and converts them to use a specified size (medium or large).
*
* Usage:
* - Dry run with large size: php resize-post-images.php
* - Dry run with medium size: php resize-post-images.php --size=medium
* - Execute with large size: php resize-post-images.php --execute
* - Execute with medium size: php resize-post-images.php --size=medium --execute
*/
// Load WordPress
require_once __DIR__ . '/wp-load.php';
// Parse command line arguments
$execute = in_array('--execute', $argv ?? []);
// Parse target size (default to 'large')
$target_size = 'large';
foreach ($argv ?? [] as $arg) {
if (strpos($arg, '--size=') === 0) {
$target_size = substr($arg, 7);
}
}
// Validate target size
if (!in_array($target_size, ['medium', 'large'])) {
echo "Error: Invalid size '{$target_size}'. Must be 'medium' or 'large'.\n";
exit(1);
}
$mode = $execute ? 'EXECUTE' : 'DRY RUN';
echo "\n========================================\n";
echo "Image Resize Script - {$mode} MODE\n";
echo "Target Size: {$target_size}\n";
echo "========================================\n\n";
if (!$execute) {
echo "Running in DRY RUN mode. No changes will be made.\n";
echo "To execute changes, run: php resize-post-images.php --size={$target_size} --execute\n\n";
}
// Get all posts
$args = [
'post_type' => 'post',
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
];
$post_ids = get_posts($args);
$total_posts = count($post_ids);
echo "Found {$total_posts} posts to process.\n\n";
$stats = [
'posts_processed' => 0,
'posts_modified' => 0,
'images_resized' => 0,
];
foreach ($post_ids as $post_id) {
$post = get_post($post_id);
$content = $post->post_content;
$original_content = $content;
$post_images_resized = 0;
// Find all wp:image blocks
// Pattern: <!-- wp:image {attributes} -->...<!-- /wp:image -->
preg_match_all('/<!-- wp:image (\{[^}]+\}) -->(.*?)<!-- \/wp:image -->/s', $content, $block_matches, PREG_SET_ORDER);
foreach ($block_matches as $match) {
$full_block = $match[0];
$attributes_json = $match[1];
$block_content = $match[2];
// Parse the JSON attributes
$attributes = json_decode($attributes_json, true);
if (!$attributes || !isset($attributes['id'])) {
continue;
}
$attachment_id = (int)$attributes['id'];
$current_size = $attributes['sizeSlug'] ?? 'full';
// Only process images that are either:
// 1) Missing sizeSlug entirely (no designation)
// 2) Explicitly set to 'full'
// Skip images already set to thumbnail, medium, or large
if (isset($attributes['sizeSlug']) && $attributes['sizeSlug'] !== 'full') {
continue;
}
// Get the target size image
$target_image = wp_get_attachment_image_src($attachment_id, $target_size);
// Skip if target size doesn't exist (image might be smaller than target size)
if (!$target_image) {
continue;
}
list($target_url, $target_width, $target_height) = $target_image;
// Get the full size to compare
$full_image = wp_get_attachment_image_src($attachment_id, 'full');
if (!$full_image) {
continue;
}
list($full_url, $full_width, $full_height) = $full_image;
// Skip if the target size is the same as full size
// (meaning the image is smaller than the target size threshold)
if ($target_url === $full_url) {
continue;
}
// Update attributes to include sizeSlug
$new_attributes = $attributes;
$new_attributes['sizeSlug'] = $target_size;
$new_attributes_json = json_encode($new_attributes, JSON_UNESCAPED_SLASHES);
// Update the figure class to include size-{target_size}
$new_block_content = $block_content;
if (preg_match('/<figure class="([^"]*)"/', $block_content, $class_match)) {
$current_classes = $class_match[1];
// Remove any existing size-* classes
$current_classes = preg_replace('/\bsize-\w+\b/', '', $current_classes);
$current_classes = trim($current_classes);
$new_classes = $current_classes . ' size-' . $target_size;
$new_classes = trim($new_classes);
$new_block_content = preg_replace(
'/<figure class="[^"]*"/',
'<figure class="' . $new_classes . '"',
$new_block_content,
1
);
} else {
// No class attribute, add one
$new_block_content = preg_replace(
'/<figure/',
'<figure class="wp-block-image size-' . $target_size . '"',
$new_block_content,
1
);
}
// Update the img src to use target size URL
// Extract current src
if (preg_match('/src="([^"]+)"/', $block_content, $src_match)) {
$current_src = $src_match[1];
$new_block_content = str_replace($current_src, $target_url, $new_block_content);
}
// Build the new block
$new_block = '<!-- wp:image ' . $new_attributes_json . ' -->' . $new_block_content . '<!-- /wp:image -->';
// Replace in content
$content = str_replace($full_block, $new_block, $content);
$post_images_resized++;
echo " - Image ID {$attachment_id}: {$current_size} → {$target_size} ({$target_width}x{$target_height})\n";
}
// Update post if content changed
if ($content !== $original_content) {
$stats['posts_modified']++;
$stats['images_resized'] += $post_images_resized;
echo "\nPost ID {$post_id}: \"{$post->post_title}\"\n";
echo " Status: {$post->post_status}\n";
echo " Images resized: {$post_images_resized}\n";
if ($execute) {
wp_update_post([
'ID' => $post_id,
'post_content' => $content,
]);
echo " ✓ Updated\n";
} else {
echo " (Would be updated in execute mode)\n";
}
echo "\n";
}
$stats['posts_processed']++;
}
// Summary
echo "\n========================================\n";
echo "SUMMARY\n";
echo "========================================\n";
echo "Posts processed: {$stats['posts_processed']}\n";
echo "Posts modified: {$stats['posts_modified']}\n";
echo "Images resized: {$stats['images_resized']}\n";
if (!$execute && $stats['posts_modified'] > 0) {
echo "\nTo apply these changes, run:\n";
echo "php resize-post-images.php --size={$target_size} --execute\n";
}
echo "\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment