Get Attachment ID from a WordPress Image URL

Taking a WordPress image URL and using it to get the attachment ID is a bit tricky. There are a lot of code samples out there that do this, but they all have problems to some degree or another.

Pippin Williamson posted a very elegant solution based on comparing the URL to the guid in the WordPress database, but that doesn’t really work if the guid gets out of sync or if the URL you have is for a cropped image.

Phillip Newcomer realized that relying on the guid wasn’t the best route and that there should be a way to get the attachment ID even when you have a cropped image URL. So, he wrote some code that strips off the image dimensions that WordPress adds to the end of the file names and then checks the resulting filename against the _wp_attached_file meta key in the database. This works well, most of the time. However, the filename after stripping off the image dimensions doesn’t always match the value of _wp_attached_file, so in those cases it fails.

If we look to WordPress StackExchange, we’ll find code written by Andrey Savchenko (a.k.a. “Rarst”) that predates both of the solutions above and actually overcomes all the issues which were previously mentioned. In fact, my solution is largely inspired by the code he wrote. The only issue with the solution he provided is that there are potentially two queries performed to find a single attachment ID.

My solution was to use the robust and sound logic that Rarst used, but to do the same work using only a single query:

So, the next time you are Googling for a code solution, don’t just use the first code sample you find… even if it works for you. Find some alternate solutions. Read the code and think about use cases where it might break. Most importantly, learn what the code does and don’t just copy and paste. We don’t need to perpetuate bad solutions out of convenience.

I’d like to challenge you to always find one thing you can improve when you decide to use a code snippet. In fact, start with mine… how would you improve it?

13 thoughts on “Get Attachment ID from a WordPress Image URL”

    1. If it is for a different protocol, then you would probably want to do a search and replace to update those instances in your database anyway. If it is a different domain, then you wouldn’t have that attachment in your database. The exception to those use cases as well as the port / auth would be if you are working on a site locally. In this case, you’ll probably want to adapt the function above so you can check against an alternate uploads URL.

  1. Awesome!
    This is what I was looking for after using Pippin Williamson solution. Not working since few days, I finally used your code snippet and it works perfectly!

    Thank you so much 🙂

  2. Worked like a charm thank you so much! Only thing worth adding a point about is you need to use the url path like http.

    And for those who use namespacing you’ll want to do this to be able to make the call. Thanks again!

    $query = new \ WP_Query( $query_args );

  3. BRILLIANT! I’ve been banging my head over this for so long. As you said, other attachment id solutions don’t always work, and I couldn’t understand why not.

    Still don’t understand how the “guid gets out of sync,” as you say, but that must be it, because I was not using cropped images.

    I’ll give our test site url below, which is where we’re implementing this for now. Soon to be on our live site, though.

    THANK YOU!!!

    1. Glad to hear you found this useful! Most often, migrating a site can get the guid out of sync because of incomplete search and replace of the domain names.

  4. Thanks for sharing this! It has been working great for me. However recently I noticed that the function gets confused if there is more than 1 file with the same name. For example:

    /uploads/2017/08/filename.jpg
    /uploads/2017/06/filename.jpg

    Is anyone else experiencing this?

    1. You make a great point Phil. The reason this is happening is that we use basename() to get the filename and use that in our query. Really, we should just be removing the base upload directory URL from the full URL and using the remainder, which will contain the proper directory path, in our query.

  5. Nice solutions, i have my own solution i query the filename directly to wp_posts, in that case i would always get the id of the image regardless if it was actually attached to any object.

    reason was that since i do it from a sync on a 3rd party system where images will have to be synced before it even had to be attached to any object.

    $id = $wpdb->getvar(“SELECT ID from wp_posts WHERE guid LIKE ‘%image.jpg%'”);

    Though i would like to know if there would be any issue if not using the wp_attachment_metadata. I also employed wp_attachment_metadata on some parts of the code but not when its running on php-cli as the script gets executed via cron.

    1. Well, filenames only have to be unique per each folder in WordPress. So unless all of your images are in one folder instead of organized into folders by year and month (the default), then you could potentially run into issues where there are actually multiple matches. See my previous comment to Phil as to why the current code snippet actually has the same issue.

      Also, unless you do more than just run that SQL query, there is no way to guarantee that the URL wasn’t for an image on another site whose base filename just happened to have the same name. As previously mentioned, you also can’t assume that the URLs in the guid field are correct either.

      One other issue you may run into is if the filename is for a cropped image. What will happen is WordPress won’t find it just by checking the guid field, which is why we are using the wp_get_attachment_metadata() approach.

      The thing I’d be careful of with a custom SQL query is security. Using the $wpdb->getvar() method directly without escaping your SQL query (see https://developer.wordpress.org/reference/classes/wpdb/prepare/) leaves you open to SQL injection attacks. Additionally, you probably don’t want to assume the table name is wp_posts either since it is possible to customize the table prefix in WordPress. You should use {$wpdb->prefix}posts or just $wpdb->posts to properly fetch the name of the posts table.

      Thanks for the question! I think this will help a lot of other people better understand the different use cases and issues at play here.

Leave a Reply

Your email address will not be published. Required fields are marked *