Whether you’re building a custom theme, setting a CSS background image, or generating Open Graph meta tags for social sharing — at some point, you’ll need to grab the featured image URL in WordPress programmatically. I’ve used every method below on real client projects and my own sites, so I’ll show you exactly which function works best for each situation.
In this guide, I’ll walk you through 5 PHP methods to retrieve the featured image URL in WordPress (plus one no-code trick), explain when to use each one, and share the real-world scenarios where each method shines.
Quick Comparison: Which Method Should You Use?
Before we dive into the code, here’s a quick overview so you can jump to the right method:
| Method | Function | Returns | Best For |
|---|---|---|---|
| 1. get_the_post_thumbnail_url() | get_the_post_thumbnail_url($post_id, $size) | URL string or false | Quick URL retrieval — most common use case |
| 2. wp_get_attachment_image_src() | wp_get_attachment_image_src($id, $size) | Array: [url, width, height, resized] | When you also need image dimensions |
| 3. wp_get_attachment_image_url() | wp_get_attachment_image_url($id, $size) | URL string or false | Cleaner alternative to Method 2 |
| 4. get_post_meta() + _thumbnail_id | get_post_meta($id, '_thumbnail_id', true) | Attachment ID (integer) | Advanced workflows needing the attachment ID first |
| 5. WordPress REST API | /wp-json/wp/v2/posts/$id?_embed | JSON object with media details | Headless WordPress, JavaScript apps, external integrations |
Let’s break each one down with code examples you can copy directly into your theme files.
* * *Prerequisites: Make Sure Your Theme Supports Featured Images
Before any of these methods work, your theme needs to have featured image support enabled. Most modern WordPress themes (Astra, GeneratePress, Flavor, Flavor Theme, etc.) have this built in. But if you’re building a custom WordPress theme from scratch, you’ll need to add this line to your theme’s functions.php file:
add_theme_support('post-thumbnails');
You can verify it’s working by checking if the “Featured Image” meta box appears on your post editor screen. If it’s there, you’re good to go.
* * *Method 1: Using get_the_post_thumbnail_url() — The Simplest Approach
This is the function I reach for 90% of the time. Introduced in WordPress 4.4, get_the_post_thumbnail_url() does exactly what the name says — it returns the featured image URL as a plain string. No arrays, no extra data, just the URL.

Basic usage inside the Loop:
if ( has_post_thumbnail() ) {
$featured_url = get_the_post_thumbnail_url( get_the_ID(), 'full' );
echo $featured_url;
}
Parameters:
$post(optional) — Post ID or WP_Post object. Defaults to the current post inside the Loop.$size(optional) — Image size:'thumbnail','medium','medium_large','large','full', or any custom size registered withadd_image_size(). Defaults to'post-thumbnail'.
Real-world example — setting a CSS background image:
<?php if ( has_post_thumbnail() ) : ?>
<div class="hero-banner" style="background-image: url('<?php echo esc_url( get_the_post_thumbnail_url( get_the_ID(), 'large' ) ); ?>');">
<h1><?php the_title(); ?></h1>
</div>
<?php endif; ?>
I use this exact pattern on custom landing pages where I need the featured image as a full-width hero background instead of a regular <img> tag.
esc_url() for security. And always check has_post_thumbnail() first — if no featured image is set, the function returns false, which will break your HTML if you don’t handle it.
* * *
Method 2: Using wp_get_attachment_image_src() — When You Need Dimensions Too
Sometimes you don’t just need the URL — you need the width and height too. For example, when generating <meta property="og:image"> tags for social sharing, Facebook and Twitter require the image dimensions alongside the URL.
That’s where wp_get_attachment_image_src() comes in. It returns an array with four values:
$thumbnail_id = get_post_thumbnail_id( get_the_ID() );
$image_data = wp_get_attachment_image_src( $thumbnail_id, 'large' );
if ( $image_data ) {
$url = $image_data[0]; // Image URL
$width = $image_data[1]; // Width in pixels
$height = $image_data[2]; // Height in pixels
$resized = $image_data[3]; // true if resized, false if original
}
Real-world example — Open Graph meta tags for social sharing:
function my_og_image_tags() {
if ( is_singular() && has_post_thumbnail() ) {
$thumb_id = get_post_thumbnail_id();
$img = wp_get_attachment_image_src( $thumb_id, 'large' );
if ( $img ) {
echo '<meta property="og:image" content="' . esc_url( $img[0] ) . '">' . "\n";
echo '<meta property="og:image:width" content="' . esc_attr( $img[1] ) . '">' . "\n";
echo '<meta property="og:image:height" content="' . esc_attr( $img[2] ) . '">' . "\n";
}
}
}
add_action( 'wp_head', 'my_og_image_tags' );
If you’re using a plugin like Rank Math or Yoast for meta tags, they handle this automatically. But for custom themes without an SEO plugin, this approach gives you full control.
* * *Method 3: Using wp_get_attachment_image_url() — The Clean One-Liner
Added in WordPress 4.4 alongside get_the_post_thumbnail_url(), this function is essentially a shortcut for Method 2 — it extracts just the URL from the attachment data array. Think of it as “I want what wp_get_attachment_image_src() gives me, but only the URL part.”
$thumbnail_id = get_post_thumbnail_id( get_the_ID() );
$image_url = wp_get_attachment_image_url( $thumbnail_id, 'large' );
if ( $image_url ) {
echo '<img src="' . esc_url( $image_url ) . '" alt="Featured Image">';
}
When would you use this over Method 1?
The difference is subtle. get_the_post_thumbnail_url() accepts a post ID directly, while wp_get_attachment_image_url() requires the attachment ID. So if you already have the attachment ID (maybe from get_post_meta() or a custom field), Method 3 saves you a step. If you just have the post ID, stick with Method 1.
Method 4: Using get_post_meta() with _thumbnail_id — The Advanced Route
Here’s what happens behind the scenes: when you set a featured image on a WordPress post, WordPress stores the attachment ID in a custom meta field called _thumbnail_id. You can query this directly.
$thumbnail_id = get_post_meta( get_the_ID(), '_thumbnail_id', true );
if ( $thumbnail_id ) {
$image_url = wp_get_attachment_url( $thumbnail_id );
echo esc_url( $image_url );
}
Why would you ever do this when Method 1 exists?
Fair question. I use this approach in two specific scenarios:
- Bulk queries with WP_Query — when fetching featured images for 50+ posts in a loop, you can use
update_post_meta_cache()to batch-load all_thumbnail_idvalues in one database query instead of hitting the database on every iteration. - Checking if a featured image exists without loading it — sometimes you just need to know if a post has a featured image set, and
get_post_meta()is lighter than callinghas_post_thumbnail()which loads extra data.
For most use cases though, Method 1 or 2 is simpler and more readable.
* * *Method 5: Using the WordPress REST API — For Headless WordPress and JavaScript Apps
If you’re building a headless WordPress site with React, Next.js, or any JavaScript framework, you won’t have access to PHP functions. Instead, you’ll use the WordPress REST API to fetch post data including the featured image.
The API endpoint:
GET /wp-json/wp/v2/posts/123?_embed
The _embed parameter is the key here — without it, you only get the featured_media ID (an integer). With _embed, WordPress includes the full media object with URLs for every registered image size.
JavaScript example using fetch:
async function getFeaturedImageURL(postId) {
const response = await fetch(
`https://yoursite.com/wp-json/wp/v2/posts/${postId}?_embed`
);
const post = await response.json();
const featuredImage = post._embedded?.['wp:featuredmedia']?.[0];
if (featuredImage) {
const fullURL = featuredImage.source_url;
const mediumURL = featuredImage.media_details?.sizes?.medium?.source_url;
console.log('Full:', fullURL);
console.log('Medium:', mediumURL);
}
}
The response includes every image size registered on your WordPress install — thumbnail, medium, medium_large, large, full, and any custom sizes. You can pick the one that fits your layout.
I used this exact approach when building a React-based front end that pulled blog posts from a WordPress backend. The _embed parameter saved me from making a second API call just to get the image data.
Bonus: How to Find the Featured Image URL Without Code (Browser Inspect)
Not a developer? You can still grab the featured image URL in about 10 seconds using your browser’s developer tools.

- Open the page where the featured image is displayed.
- Right-click on the image and select Inspect (Chrome/Edge) or Inspect Element (Firefox).
- In the Elements panel, find the
<img>tag — the featured image URL is in thesrcattribute. - Right-click the URL and select Copy link address.
This works great for a one-time grab, but obviously it’s not a solution for dynamically retrieving URLs in your theme code.
* * *WordPress Image Sizes Reference
Every method above accepts a $size parameter. Here are the default WordPress image sizes as of WordPress 6.7 (April 2026):
| Size Name | Max Width | Max Height | Cropped? | When to Use |
|---|---|---|---|---|
thumbnail | 150px | 150px | Yes (hard crop) | Small thumbnails, post grids, widgets |
medium | 300px | 300px | No (proportional) | Sidebar images, small content images |
medium_large | 768px | 0 (auto) | No | Responsive images, srcset |
large | 1024px | 1024px | No (proportional) | Main content images, hero sections |
full | Original | Original | No | Full-resolution downloads, backgrounds |
You can also register custom sizes in your theme’s functions.php:
add_image_size( 'hero-banner', 1920, 600, true ); // Hard crop
add_image_size( 'card-thumb', 400, 250, true ); // Hard crop
After registering a custom size, you’ll need to regenerate your existing thumbnails using a plugin like an image optimization tool or the Regenerate Thumbnails plugin. New uploads will use the custom size automatically.
> **Performance tip:** Always use the smallest image size that fits your layout. Loading afull size (often 2000px+) image for a 300px sidebar thumbnail wastes bandwidth and slows down your page. Use 'medium' or 'large' instead.
* * *
How to Get Featured Image URL Outside the Loop
All the methods above work inside the WordPress Loop. But what if you need the featured image URL in a widget, sidebar, footer, or a completely separate template file?
You just need to pass the post ID explicitly:
// Get featured image URL for post ID 42, outside the Loop
$url = get_the_post_thumbnail_url( 42, 'large' );
if ( $url ) {
echo '<img src="' . esc_url( $url ) . '" alt="Post 42 featured image">';
}
If you’re on an archive page and want to display each post’s featured image outside the standard Loop structure, grab the ID from the query:
$recent_posts = get_posts( array( 'numberposts' => 5 ) );
foreach ( $recent_posts as $post ) {
$img_url = get_the_post_thumbnail_url( $post->ID, 'medium' );
if ( $img_url ) {
echo '<img src="' . esc_url( $img_url ) . '" alt="' . esc_attr( $post->post_title ) . '">';
}
}
* * *
What If the Post Has No Featured Image? (Fallback Handling)
One mistake I see in a lot of themes: they assume every post has a featured image set. When it doesn’t, the function returns false, and you end up with broken <img> tags or empty background images.
Here’s a simple fallback pattern I use on every project:
function get_featured_or_fallback( $post_id = null, $size = 'large' ) {
$url = get_the_post_thumbnail_url( $post_id, $size );
if ( ! $url ) {
$url = get_template_directory_uri() . '/images/default-featured.jpg';
}
return esc_url( $url );
}
Drop this in your functions.php and call get_featured_or_fallback() anywhere you’d normally use get_the_post_thumbnail_url(). If the post has a featured image, you get that. If not, you get your default placeholder. No broken layouts.
Displaying the Featured Image vs. Getting the URL
Quick clarification since I see this confusion a lot: there’s a difference between getting the URL (a string you can use anywhere) and displaying the image (outputting a full <img> tag).
If you just want to display the featured image with a proper <img> tag, WordPress has built-in functions for that:
// Display the featured image (outputs a full <img> tag)
the_post_thumbnail( 'large' );
// Get the <img> tag as a string (doesn't echo it)
$img_tag = get_the_post_thumbnail( get_the_ID(), 'large' );
These functions output a complete <img> tag with proper srcset, sizes, alt, and class attributes — which is actually better for responsive images and accessibility. Use the URL methods (covered in this guide) only when you need the raw URL for CSS backgrounds, meta tags, API responses, or passing to JavaScript.
If you want to control how the featured image appears on your posts (or hide the featured image entirely on certain posts), check out my detailed guide on that.
* * *Frequently Asked Questions
What is the easiest way to get the featured image URL in WordPress?
Use get_the_post_thumbnail_url( get_the_ID(), 'full' ) inside the Loop. It returns the URL as a plain string — no arrays or extra processing needed. This function was added in WordPress 4.4 and works with all modern themes.
Can I get the featured image URL outside the WordPress Loop?
Yes. Pass the post ID directly as the first parameter: get_the_post_thumbnail_url( 42, 'large' ). This works in sidebars, widgets, footers, or any template file regardless of the Loop context.
How do I get the featured image URL with width and height dimensions?
Use wp_get_attachment_image_src( get_post_thumbnail_id(), 'large' ). It returns an array where index 0 is the URL, index 1 is the width, and index 2 is the height. This is useful for Open Graph meta tags that require image dimensions.
How do I get the featured image URL in the WordPress REST API?
Add the _embed parameter to your API request: /wp-json/wp/v2/posts/123?_embed. The featured image data will be in _embedded['wp:featuredmedia'][0] with URLs for every registered image size.
What happens if no featured image is set on the post?
All featured image functions return false when no image is set. Always check with has_post_thumbnail() or a simple if statement before using the URL to avoid broken HTML. You can set a fallback default image in your theme’s functions.php.
What image sizes can I pass to get_the_post_thumbnail_url()?
WordPress includes five default sizes: thumbnail (150×150), medium (300×300), medium_large (768×auto), large (1024×1024), and full (original). You can also use custom sizes registered with add_image_size() in your theme.
Summing Up!
For most WordPress projects, get_the_post_thumbnail_url() is all you need. It’s clean, it’s simple, and it just works. Grab Method 2 when you need image dimensions (social sharing meta tags), and Method 5 when you’re building headless WordPress apps with JavaScript.
The one thing I’ll stress: always check if the featured image exists before using the URL. A quick has_post_thumbnail() check saves you from broken layouts and empty <img> tags down the road.
Got a specific use case where none of these methods work? Drop a comment below and I’ll help you find the right approach for your setup!