wp_get_attachment_url( int $attachment_id = 0 ): string|false
- Since
- 2.1.0
- Source
wp-includes/post.php:7191
Builds the public URL for an attachment post by reading its `_wp_attached_file` post meta and combining it with the uploads directory. Falls back to the attachment's GUID when the meta is missing or the uploads path can't be resolved, so the returned string isn't always a guaranteed-current path. Pair it with `wp_get_attachment_image_src()` when you also need width and height, since this function only ever returns the URL.
Compatibility
- WordPress
- since 2.1.0
- PHP
- 7.4–8.6-dev
- 6.7.7
- 6.8.8
- 6.9.7
- 7.0.4
- 7.1.0
Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.
Parameters
$attachment_idintoptional- Attachment post ID. Defaults to global $post.Default:
0
Return value
string|false- Attachment URL, otherwise false.
Code examples
Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.
Get the direct URL for an attachment by ID
Insert an attachment post with attached-file meta so the sandbox has something to resolve a URL for.
$attachment_id = get_option( 'wppaste_example_attachment_id' );
$url = wp_get_attachment_url( $attachment_id );
if ( $url ) {
echo esc_html( 'Attachment URL: ' . $url );
} else {
echo esc_html( 'No URL could be resolved for this attachment.' );
}In a real upload the file would already exist on disk; here only the meta is set, which is enough for the URL-building logic to run.
Understand why the function returns false for a non-attachment post
Pass an ordinary post ID from the baseline fixtures to see the false return path in action.
$url = wp_get_attachment_url( 1 );
if ( false === $url ) {
echo esc_html( 'Post 1 is not an attachment, so wp_get_attachment_url() returned false.' );
} else {
echo esc_html( $url );
}Common problems and fixes · 4
- Why does wp_get_attachment_url() return false when I know the ID exists?
- Why does wp_get_attachment_url() give me the GUID instead of a proper uploads URL?
- Why is the URL still http on the front end when my site is over SSL?
- Why does calling wp_get_attachment_url() with no argument return false outside The Loop?
Why does wp_get_attachment_url() return false when I know the ID exists?
$post->post_type and returns false unless it's exactly 'attachment'. Passing the ID of a regular post, page, or any other post type always fails this check even though the post itself is valid.Why does wp_get_attachment_url() give me the GUID instead of a proper uploads URL?
_wp_attached_file post meta and the uploads directory. If that meta is empty or wp_get_upload_dir() reports an error, it silently falls back to get_the_guid(), which can be stale if the site URL ever changed.Why is the URL still http on the front end when my site is over SSL?
set_url_scheme() is only applied when is_ssl() is true and the request is neither admin nor wp-login.php. On admin screens or admin-ajax requests the URL keeps whatever scheme was stored, which can mismatch a front end served over https.Why does calling wp_get_attachment_url() with no argument return false outside The Loop?
$attachment_id at its default 0 makes the function rely on get_post( 0 ), which in turn falls back to the global $post. Outside a post context (an admin page, a cron job, a shortcode called from a widget) that global may be unset, so get_post() returns null and the function bails out.Alternatives and related functions
wp_get_attachment_image_src- When you also need the image width and height alongside the URL, not just the URL string.
wp_get_attachment_image- When you want a ready-made `` tag with srcset and sizes instead of building markup around a bare URL.
get_attached_file- When you need the local filesystem path to the attachment rather than its public URL.
wp_get_original_image_url- When the attachment may have been edited or scaled and you specifically need the URL of the original, unmodified file.
Performance profile
How much work a call to wp_get_attachment_url() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.
- Cost class
- Heavy
- Scaling
- Constant
- Instructions
- 10–78
- Plugin surface
- 1 hook
- Called by
- 43
Reaches the database via get_post().
No loop in the body: the same number of instructions runs whatever you pass in.
Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 91.
Third-party callbacks on 'wp_get_attachment_url' run inside this call, and their cost is not bounded by anything here.
43 places in core call this, so the cost is paid more often than your own code shows.
What it touches
- querycontent query
get_post()called directly - hookthird-party callbacks
apply_filters()called directly
Further down the call graph this can also reach cache, serialize, option and transient. Those are the worst case, several calls deep and usually down an error path, not what a normal call pays.
What one call costs · 19 distinct outcomes
One number would be a lie: the work depends on which branch runs. These are every distinct cost wp_get_attachment_url() can have, taken from its control-flow graph on PHP 8.5.
| When | Instructions | Calls it makes |
|---|---|---|
| always | 10–13 | get_post() |
!is_ssl() | 36 | get_post(), get_post_meta(), is_ssl(), apply_filters() |
is_ssl() | 39–41 | get_post(), get_post_meta(), is_ssl(), is_admin(), apply_filters() |
!is_ssl() | 40–52 | get_post(), get_post_meta(), wp_get_upload_dir(), is_ssl(), apply_filters() |
!is_ssl() | 42 | get_post(), get_post_meta(), get_the_guid(), is_ssl(), apply_filters() |
is_ssl() | 43–57 | get_post(), get_post_meta(), wp_get_upload_dir(), is_ssl(), is_admin(), apply_filters() |
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 45 | get_post(), get_post_meta(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
is_ssl() | 45–47 | get_post(), get_post_meta(), get_the_guid(), is_ssl(), is_admin(), apply_filters() |
!is_ssl() | 46–58 | get_post(), get_post_meta(), wp_get_upload_dir(), get_the_guid(), is_ssl(), apply_filters() |
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 49–61 | get_post(), get_post_meta(), wp_get_upload_dir(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
is_ssl() | 49–63 | get_post(), get_post_meta(), wp_get_upload_dir(), get_the_guid(), is_ssl(), is_admin(), apply_filters() |
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 51 | get_post(), get_post_meta(), get_the_guid(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
7 further outcomes, up to 78 instructions
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 55–67 | get_post(), get_post_meta(), wp_get_upload_dir(), get_the_guid(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
!is_ssl() | 63 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), is_ssl(), apply_filters() |
is_ssl() | 66–68 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), is_ssl(), is_admin(), apply_filters() |
!is_ssl() | 69 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), get_the_guid(), is_ssl(), apply_filters() |
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 72 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
is_ssl() | 72–74 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), get_the_guid(), is_ssl(), is_admin(), apply_filters() |
is_ssl() && !is_admin() && $pagenow !== "wp-login.php" | 78 | get_post(), get_post_meta(), wp_get_upload_dir(), _wp_get_attachment_relative_path(), trailingslashit(), wp_basename(), get_the_guid(), is_ssl(), is_admin(), set_url_scheme(), apply_filters() |
Across PHP versions
| PHP | Compiled | Executed | Branches | Notes |
|---|---|---|---|---|
| 8.6-dev | 91 | 10–78 | 12 | |
| 8.5 | 91 | 10–78 | 12 | |
| 8.4 | 91 | 10–78 | 12 | 9 fewer instructions than PHP 8.3 |
| 8.3 | 100 | 10–84 | 12 | |
| 8.2 | 100 | 10–84 | 12 | |
| 8.1 | 100 | 10–84 | 12 | 1 fewer instruction than PHP 7.4 |
| 7.4 | 101 | 10–85 | 12 |
An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.
Hooks and filters fired · 1
One hook fires while wp_get_attachment_url() runs, in this order:
- apply_filters( wp_get_attachment_url )filterline 7248 (+57 into the body)
Filters the attachment URL.
Uses · 13
- get_post()Retrieves post data given a post ID or post object.
- get_post_meta()Retrieves a post meta field for the given post ID.
- wp_get_upload_dir()Retrieves uploads directory information.
- str_starts_with()Polyfill for `str_starts_with()` function added in PHP 8.0.
- str_contains()Polyfill for `str_contains()` function added in PHP 8.0.
- trailingslashit()Appends a trailing slash.
- _wp_get_attachment_relative_path()Gets the attachment path relative to the upload directory.
- wp_basename()i18n-friendly version of basename().
- get_the_guid()Retrieves the Post Global Unique Identifier (guid).
- is_ssl()Determines if SSL is used.
- is_admin()Determines whether the current request is for an administrative interface page.
- set_url_scheme()Sets the scheme for a URL.
Show all 13
- apply_filters()Calls the callback functions that have been added to a filter hook.
Used by · 43
- Custom_Image_Header::ajax_header_crop()Gets attachment uploaded by Media Manager, crops it, then saves it as a new object. Returns JSON-encoded object details.
- Custom_Image_Header::create_attachment_object()Creates an attachment 'object'.
- Custom_Image_Header::step_3()Displays third step of custom header image page.
- WP_Customize_Manager::import_theme_starter_content()Imports theme starter content into the customized state.
- WP_Media_List_Table::_get_row_actions()Gets the row actions for a media item.
- WP_REST_Attachments_Controller::prepare_item_for_response()Prepares a single attachment output for response.
- WP_Site_Icon::create_attachment_object()Creates an attachment 'object'.
- WP_Widget_Media_Audio::render_media()Render the media on the frontend.
- WP_Widget_Media_Image::render_media()Render the media on the frontend.
- WP_Widget_Media_Video::render_media()Render the media on the frontend.
- _delete_attachment_theme_mod()Checks an attachment being deleted to see if it's a header or background image.
- _load_image_to_edit_path()Retrieves the path or URL of an attachment's attached file.
Show all 43
- attachment_submitbox_metadata()Displays non-editable attachment metadata in the publish meta box.
- block_core_gallery_dynamic_image_link_attributes()Builds the link-related image block attributes for a dynamically rendered gallery image, mapping the gallery-wide `linkTo` setting onto a single image.
- block_core_image_render_lightbox()Adds the directives and layout needed for the lightbox behavior.
- edit_form_image_editor()Displays the image and editor in the post editor
- export_wp()Generates the WXR export file for download.
- get_attachment_fields_to_edit()Retrieves the attachment fields to edit form fields.
- get_attachment_icon_src()Retrieve icon URL and Path.
- get_header_video_url()Retrieves header video URL for custom header.
- get_media_states()Retrieves an array of media states from an attachment.
- get_post_galleries()Retrieves galleries from the passed post's content.
- get_the_attachment_link()Retrieve HTML content of attachment image with link.
- get_uploaded_header_images()Gets the header images uploaded for the active theme.
- image_downsize()Scales an image to fit a particular size (such as 'thumb' or 'medium').
- image_get_intermediate_size()Retrieves the image's intermediate size (resized) path, width, and height.
- image_link_input_fields()Retrieves HTML for the Link URL buttons with the default link type as specified.
- media_sideload_image()Downloads an image from the specified URL, saves it as an attachment, and optionally attaches it to a post.
- prepend_attachment()Wraps attachment in paragraph tag before content.
- redirect_canonical()Redirects incoming links to the proper URL based on the site url.
- twentyfifteen_entry_meta()Prints HTML with meta information for the categories, tags.
- twentyfourteen_the_attached_image()Prints the attached image with a link to the next attached image.
- twentythirteen_the_attached_image()Prints the attached image with a link to the next attached image.
- wp_audio_shortcode()Builds the Audio shortcode output.
- wp_copy_parent_attachment_properties()Copy parent attachment properties to newly cropped image.
- wp_get_attachment_link()Retrieves an attachment page link using an image or icon, if possible.
- wp_get_original_image_url()Retrieves the URL to an original attachment image.
- wp_media_personal_data_exporter()Finds and exports attachments associated with an email address.
- wp_playlist_shortcode()Builds the Playlist shortcode output.
- wp_prepare_attachment_for_js()Prepares an attachment post object for JS, where it is expected to be JSON-encoded and fit into an Attachment model.
- wp_save_image()Saves image to post, along with enqueued changes in `$_REQUEST['history']`.
- wp_video_shortcode()Builds the Video shortcode output.
- wp_xmlrpc_server::_prepare_media_item()Prepares media item data for return in an XML-RPC object.
Source code
function wp_get_attachment_url( $attachment_id = 0 ) { global $pagenow; $attachment_id = (int) $attachment_id; $post = get_post( $attachment_id ); if ( ! $post ) { return false; } if ( 'attachment' !== $post->post_type ) { return false; } $url = ''; // Get attached file. $file = get_post_meta( $post->ID, '_wp_attached_file', true ); if ( $file ) { // Get upload directory. $uploads = wp_get_upload_dir(); if ( $uploads && false === $uploads['error'] ) { // Check that the upload base exists in the file location. if ( str_starts_with( $file, $uploads['basedir'] ) ) { // Replace file location with url location. $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file ); } elseif ( str_contains( $file, 'wp-content/uploads' ) ) { // Get the directory name relative to the basedir (back compat for pre-2.7 uploads). $url = trailingslashit( $uploads['baseurl'] . '/' . _wp_get_attachment_relative_path( $file ) ) . wp_basename( $file ); } else { // It's a newly-uploaded file, therefore $file is relative to the basedir. $url = $uploads['baseurl'] . "/$file"; } } } /* * If any of the above options failed, Fallback on the GUID as used pre-2.7, * not recommended to rely upon this. */ if ( ! $url ) { $url = get_the_guid( $post->ID ); } // On SSL front end, URLs should be HTTPS. if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $pagenow ) { $url = set_url_scheme( $url ); } /** * Filters the attachment URL. * * @since 2.1.0 * * @param string $url URL for the given attachment. * @param int $attachment_id Attachment post ID. */ $url = apply_filters( 'wp_get_attachment_url', $url, $post->ID ); if ( ! $url ) { return false; } return $url;}Changelog
Introduced in 2.1.0. Unchanged from 6.7.7 through 7.1.0.
Signature, return type and hooks compared across 5 parsed releases.
About this page
- Parsed data
- Generated from the wordpress-develop 7.1.0 tag, from
src/wp-includes/post.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it. - Corrections
- Something wrong on this page? Report it and it gets fixed in the next regeneration.