TimThumb is dead - how to handle images in WordPress (2026 guide)

TimThumb is dead - how to handle images in WordPress (2026 guide)

Last verified: September 22, 2026
8 min read
Guide
Full-stack developer
Security auditor

TimThumb sat in almost every commercial WordPress theme around 2010. Drop one PHP file in the theme, pass src, w, and h on the query string, and you got a cropped JPEG without touching the Media Library. That convenience is why the script spread, and why the 2011 remote code execution wave hit so many sites at once.

In 2026 there is no reason to keep it. WordPress writes intermediate sizes at upload, browsers pick a width from srcset, and CDNs can transform a master file at the edge. If timthumb.php (or a renamed thumb.php) still lives under wp-content, treat it as an open door, not a feature.

#Why TimThumb died

TimThumb did not fail because cropping went out of fashion. It failed because the design assumed a theme could safely fetch, resize, and cache images from URLs while running as PHP on the same host that served the site.

The 2011 flaw let attackers abuse writable cache paths and weak validation so the script wrote executable content the web server would later run. Patch waves followed, then forks, then silence. Themes kept shipping copies under new filenames. Scanners still find them on inherited projects years later because nobody owned the cleanup once the marketing site launched.

Three structural problems never went away:

  1. Security. A public PHP endpoint that accepts a remote or local src and writes under a cache directory is a classic RCE surface when path checks slip.
  2. Performance. First request paid a PHP and GD (or Imagick) tax. Cache misses under traffic turned the origin into an image factory.
  3. No Media Library contract. TimThumb ignored attachment metadata. WordPress could not track crops, regenerate after a theme change, or emit responsive markup from a single source of truth.

Core media closed that gap. Uploads land under /wp-content/uploads/, WordPress records width and height, and registered sizes become real files next to the original. Display code reads those files. Nothing in the theme needs to open a URL, resize it, and hope the cache directory stays locked down.

If you are auditing a client handoff, treat any theme-level resize endpoint the same way you treat an open eval on $_GET: remove it, then prove images still resolve through attachments. Security scanners flag TimThumb by filename; attackers also probe renamed copies, so filename hygiene alone is not enough.

#Core image sizes

WordPress ships default intermediates (thumbnail, medium, medium_large, large) and lets themes register more with add_image_size(). Those are the replacements for TimThumb query strings: declare the crops your layouts need, generate them once, serve static files forever after.

Register sizes on after_setup_theme:

function wppoland_setup_theme() {
    add_theme_support( 'post-thumbnails' );

    // Soft crop - keep aspect ratio, fit inside the box.
    add_image_size( 'blog-list', 800, 400 );

    // Hard crop - exact box from the center.
    add_image_size( 'team-member', 300, 300, true );

    // Hard crop with focus toward the top of the frame.
    add_image_size( 'hero-banner', 1920, 600, array( 'center', 'top' ) );
}
add_action( 'after_setup_theme', 'wppoland_setup_theme' );

In a template, prefer the attachment helpers so WordPress can attach classes, alt text, and responsive attributes:

if ( has_post_thumbnail() ) {
    the_post_thumbnail( 'hero-banner', array( 'class' => 'img-fluid' ) );
}

Or, when you already hold an attachment ID:

echo wp_get_attachment_image( $attachment_id, 'blog-list' );

Sizes registered after files were uploaded do not appear until you regenerate. On a small library the Regenerate Thumbnails plugin is enough. On anything large, use WP-CLI so the job can run without a browser timeout:

wp media regenerate --yes

Keep the size list short. Every extra crop multiplies disk use. Register what the theme and content models actually render - list cards, author avatars, heroes - and drop dead names when a redesign retires a layout. You can inspect registered intermediates with wp_get_intermediate_image_sizes() when auditing a theme you did not write.

The win versus TimThumb is simple: generation happens at upload (or regenerate) time. Page views serve files from disk or CDN cache. Origin PHP is not in the resize path.

Editors should still upload a high-quality master that is large enough for the biggest crop you register. Soft crops cannot invent missing pixels. Hard crops need composition awareness: a headshot registered as center, top behaves differently from a landscape product shot cropped from the dead center. Document those choices in the theme README so the next developer does not reintroduce a TimThumb-style “just pass w and h” shortcut.

#Srcset and the sizes attribute

Modern WordPress does not stop at one derivative. When you print an image through wp_get_attachment_image() or the_post_thumbnail(), core builds a srcset from available widths and a default sizes hint. The browser then downloads a file close to the display width instead of always pulling the hero crop on a phone.

Rough shape of what core emits:

<img
  src="image-800x400.jpg"
  srcset="image-300x150.jpg 300w,
          image-800x400.jpg 800w,
          image-1024x512.jpg 1024w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt=""
/>

Your job as a theme developer is usually not to hand-write srcset. It is to:

  • Register sensible intermediates so the set has useful steps.
  • Pass an accurate sizes value when the default is wrong for a layout (full-bleed hero versus a third-column card).
  • Filter wp_calculate_image_sizes when a component always sits in a known CSS width.

TimThumb could not do this. It produced one URL per request. Responsive delivery depended on either shipping oversized files or inventing more query-string variants. Core metadata plus srcset is the maintained path, documented with the rest of WordPress media administration.

For LCP images, still be explicit: preload or mark the hero with fetchpriority="high" where appropriate, and avoid lazy-loading the first viewport image. Responsive markup helps bandwidth; it does not replace choosing a sane primary src.

When debugging “why is the phone still downloading the 1920px file,” inspect the rendered sizes attribute first. A wrong 100vw on a 320px-wide card forces the browser to pick a huge candidate even when smaller intermediates exist. Fix the hint; do not bring back a theme PHP resizer to paper over bad markup.

#Modern CDN transforms

Native sizes cover most editorial layouts. Design systems, ad slots, and A/B heroes sometimes need widths you never registered. Putting that work back into a theme PHP script recreates TimThumb. Push it to the CDN instead.

Hosts and edge platforms commonly accept width, height, fit, and format on a URL or via an image worker in front of /wp-content/uploads/. Conceptually:

<img
  src="https://cdn.example.com/uploads/2026/09/hero.jpg?width=800&height=400&format=avif"
  alt=""
  width="800"
  height="400"
/>

The origin keeps one master (or the WordPress full size). The edge caches each transform. You keep security boundaries: the CDN only reads objects you already published under uploads, not arbitrary remote src values from a query string on your theme.

When you adopt transforms:

  • Prefer one pipeline. Either WordPress intermediates for known crops, or CDN transforms for fluid sizes - not both fighting over the same markup.
  • Keep format negotiation at the edge (AVIF/WebP with JPEG fallback) so editors can still upload JPEG from cameras and phones.
  • Invalidate or version when you replace a master file so long-lived edge caches do not serve yesterday’s crop.

WordPress already accepts WebP (since 5.8) and AVIF (since 6.5) as upload formats. You do not need TimThumb-era optimizer plugins solely to invent a resize endpoint. Use core for library truth; use the CDN when you need elastic dimensions.

Measure after the switch. Compare bytes for the LCP image and the median content image before and after. If edge transforms add latency on first hit, warm the common widths or keep WordPress intermediates for the above-the-fold set and reserve the CDN for long-tail sizes. The goal is predictable delivery, not a clever URL grammar.

#Removing TimThumb from an inherited site

On any theme or plugin dump you did not author:

  1. Search wp-content for timthumb.php, thumb.php, and strings like timthumb.php?src=.
  2. Delete the script after templates no longer call it.
  3. Replace markup:
// Old - do not ship.
echo '<img src="' . esc_url( get_template_directory_uri() . '/timthumb.php?src=' . $url ) . '" alt="" />';

// New - Media Library path.
the_post_thumbnail( 'blog-list' );
  1. Regenerate intermediates, purge CDN cache for old TimThumb URLs, and re-check the homepage LCP image.

Need a second pair of eyes on an old theme still calling resize scripts? Talk to a WordPress developer who can strip TimThumb, register the sizes your layouts need, and wire CDN transforms without leaving a PHP endpoint on the origin.

#Practical checklist for 2026

  • No timthumb.php / thumb.php in themes or mu-plugins.
  • Featured images and content images go through attachment APIs.
  • Size names match real templates; unused sizes removed.
  • srcset present on content images; sizes corrected for tight layouts.
  • On-the-fly needs live on the CDN, not in theme PHP.
  • WebP/AVIF in the delivery path where the stack supports them.

TimThumb taught the ecosystem that “convenient resize in the theme” is a security and ops debt. Core intermediate sizes, srcset, and edge transforms are the maintained stack. Use them and leave the 2011 script in the history books.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Article FAQ

Frequently asked questions

Practical answers to apply the topic in real execution.

SEO-readyGEO-readyAEO-ready4 Q&A
Why is TimThumb considered insecure in 2026?#
TimThumb was hit by a critical remote code execution flaw in 2011 tied to writable cache directories and weak path checks. The script is unmaintained. Core media APIs close that attack surface by writing intermediate files under uploads instead of fetching and rewriting arbitrary URLs through a theme PHP endpoint.
How do I replace TimThumb with core image sizes?#
Delete timthumb.php, register the crops you need with add_image_size, swap template markup to the_post_thumbnail or wp_get_attachment_image, then run wp media regenerate so existing attachments get the new intermediates. Confirm no theme or plugin still builds a /timthumb.php?src= URL.
Do I still need on-the-fly resizing?#
Usually no on the origin. WordPress already stores intermediate sizes and builds srcset. When you need arbitrary dimensions for ads or design systems, push the transform to a CDN that resizes at the edge from a single master file, not a PHP script inside the theme.
What breaks if I delete TimThumb without regenerating?#
Templates that still point at timthumb.php return 404s or broken images. Layouts that relied on hard crops may look wrong until intermediates exist. Regenerate first or in the same deploy window, then remove the script.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles