Cloudinary Blog

How to dynamically create SEO friendly URLs for your site's images

Dynamically create SEO friendly URLs for your images

Image URLs tend to appear as a long list of random characters that are not intended for viewers and are not very useful to search engines. Concise and meaningful image file names are better for search engines to extract information about an image, therefore supporting your site's SEO ranking.

Often times, users' uploaded image files do not have descriptive names. This creates a great challenge for developers and site content managers who need to maintain SEO friendly, short, and meaningful URLs. Cloudinary helped tackle this issue by offering users two new features: Root Path URL and Dynamic SEO suffixes. These features can be useful for website as well as web application owners, and are especially recommended for content-heavy sites, like online magazines and news sites.

Root Path URLs

Cloudinary's image URLs are delivered via CDN and use folder names as their path prefixes. These include resource and image types, such as /image/upload and /raw/upload. The most popular prefix among Cloudinary’s users includes /image/upload in its URL. Now, with Cloudinary’s Root Path URL feature, the /image/upload section of URLs can be removed, solely leaving the image’s public ID (file name) at the path’s root.

Below is an example of an image that was uploaded to Cloudinary and was assigned basketball_shot as the public ID:

And here is an example of a Cloudinary image URL that uses the Root Path URL feature:

Ruby:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", :use_root_path=>true)
PHP v1:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", array("use_root_path"=>true))
PHP v2:
Copy to clipboard
(new ImageTag('basketball_shot.jpg'))
  ->useRootPath(true);
Python:
Copy to clipboard
CloudinaryImage("basketball_shot.jpg").image(use_root_path=True)
Node.js:
Copy to clipboard
cloudinary.image("basketball_shot.jpg", {use_root_path: true})
Java:
Copy to clipboard
cloudinary.url().useRootPath(true).imageTag("basketball_shot.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('basketball_shot.jpg', {useRootPath: true}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("basketball_shot.jpg", {use_root_path: true})
React:
Copy to clipboard
<Image publicId="basketball_shot.jpg" useRootPath="true">

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="basketball_shot.jpg" useRootPath="true">

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="basketball_shot.jpg" use-root-path="true">

</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.UseRootPath(true).BuildImageTag("basketball_shot.jpg")
Android:
Copy to clipboard
MediaManager.get().url().useRootPath(true).generate("basketball_shot.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setUseRootPath( true).generate("basketball_shot.jpg")!, cloudinary: cloudinary)

Both URLs yield the same uploaded image:

Basketball shot sample image

With the Root Path URL capability, users can also add parameters for on-the-fly image manipulation. For example, if an uploaded image needs to be cropped to 200 x 200 pixels, it can be transformed simply by setting the width and height parameters to 200 and the crop mode to 'fill':

Ruby:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", :width=>200, :height=>200, :crop=>"fill", :use_root_path=>true)
PHP v1:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", array("width"=>200, "height"=>200, "crop"=>"fill", "use_root_path"=>true))
PHP v2:
Copy to clipboard
(new ImageTag('basketball_shot.jpg'))
  ->resize(Resize::fill()->width(200)->height(200))
  ->useRootPath(true);
Python:
Copy to clipboard
CloudinaryImage("basketball_shot.jpg").image(width=200, height=200, crop="fill", use_root_path=True)
Node.js:
Copy to clipboard
cloudinary.image("basketball_shot.jpg", {width: 200, height: 200, crop: "fill", use_root_path: true})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().width(200).height(200).crop("fill")).useRootPath(true).imageTag("basketball_shot.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('basketball_shot.jpg', {width: 200, height: 200, crop: "fill", useRootPath: true}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("basketball_shot.jpg", {width: 200, height: 200, crop: "fill", use_root_path: true})
React:
Copy to clipboard
<Image publicId="basketball_shot.jpg" useRootPath="true">
  <Transformation width="200" height="200" crop="fill" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="basketball_shot.jpg" useRootPath="true">
  <cld-transformation width="200" height="200" crop="fill" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="basketball_shot.jpg" use-root-path="true">
  <cl-transformation width="200" height="200" crop="fill">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(200).Height(200).Crop("fill")).UseRootPath(true).BuildImageTag("basketball_shot.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().width(200).height(200).crop("fill")).useRootPath(true).generate("basketball_shot.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setUseRootPath( true).setTransformation(CLDTransformation().setWidth(200).setHeight(200).setCrop("fill")).generate("basketball_shot.jpg")!, cloudinary: cloudinary)
200x200 basketball shot thumbnail

When using Cloudinary's client libraries (SDKs) to build delivery URLs and to add image tags, you simply need to set the new parameter, use_root_path, to true.

The following code sample was used to create an HTML image tag with an image URL using the Root Path URL feature:

Ruby:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", :width=>200, :height=>200, :crop=>"fill", :use_root_path=>true)
PHP v1:
Copy to clipboard
cl_image_tag("basketball_shot.jpg", array("width"=>200, "height"=>200, "crop"=>"fill", "use_root_path"=>true))
PHP v2:
Copy to clipboard
(new ImageTag('basketball_shot.jpg'))
  ->resize(Resize::fill()->width(200)->height(200))
  ->useRootPath(true);
Python:
Copy to clipboard
CloudinaryImage("basketball_shot.jpg").image(width=200, height=200, crop="fill", use_root_path=True)
Node.js:
Copy to clipboard
cloudinary.image("basketball_shot.jpg", {width: 200, height: 200, crop: "fill", use_root_path: true})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().width(200).height(200).crop("fill")).useRootPath(true).imageTag("basketball_shot.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('basketball_shot.jpg', {width: 200, height: 200, crop: "fill", useRootPath: true}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("basketball_shot.jpg", {width: 200, height: 200, crop: "fill", use_root_path: true})
React:
Copy to clipboard
<Image publicId="basketball_shot.jpg" useRootPath="true">
  <Transformation width="200" height="200" crop="fill" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="basketball_shot.jpg" useRootPath="true">
  <cld-transformation width="200" height="200" crop="fill" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="basketball_shot.jpg" use-root-path="true">
  <cl-transformation width="200" height="200" crop="fill">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(200).Height(200).Crop("fill")).UseRootPath(true).BuildImageTag("basketball_shot.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().width(200).height(200).crop("fill")).useRootPath(true).generate("basketball_shot.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setUseRootPath( true).setTransformation(CLDTransformation().setWidth(200).setHeight(200).setCrop("fill")).generate("basketball_shot.jpg")!, cloudinary: cloudinary)

Dynamic SEO Suffixes

Many of our users have requested a Cloudinary capability that creates image URLs that are more comprehensive and descriptive. Each image uploaded to Cloudinary is given a public ID, which is its name for delivery URLs. Cloudinary already offers the ability to define custom public IDs with a string of text or multiple folder names (separated by slashes), while uploading images. These public IDs can be as descriptive as necessary.

Our new feature allows you to separate the process of uploading an image and assigning a public ID from creating descriptive URLs. If an image is not given a suitable name during the upload process, you will be able to assign additional URLs to the image afterwards. For example, with this feature, you can dynamically add multiple different suffixes to create as many descriptive URLs as necessary for your site. You may want to use these URLs to support different languages for a single image or to reflect specific content on certain pages.

To add a dynamic SEO suffix, an image’s path prefix must first be changed from the default /image/upload to the shorter version /images.

Here is an example of an image that was uploaded with the ID tepu4mm0qzw6lkfxt1m and is delivered by the following CDN optimized URL using a standard path prefix:

Ruby:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg")
PHP v1:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg")
PHP v2:
Copy to clipboard
(new ImageTag('ltepu4mm0qzw6lkfxt1m.jpg'));
Python:
Copy to clipboard
CloudinaryImage("ltepu4mm0qzw6lkfxt1m.jpg").image()
Node.js:
Copy to clipboard
cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg")
Java:
Copy to clipboard
cloudinary.url().imageTag("ltepu4mm0qzw6lkfxt1m.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('ltepu4mm0qzw6lkfxt1m.jpg').toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg")
React:
Copy to clipboard
<Image publicId="ltepu4mm0qzw6lkfxt1m.jpg" >

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="ltepu4mm0qzw6lkfxt1m.jpg" >

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="ltepu4mm0qzw6lkfxt1m.jpg" >

</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.BuildImageTag("ltepu4mm0qzw6lkfxt1m.jpg")
Android:
Copy to clipboard
MediaManager.get().url().generate("ltepu4mm0qzw6lkfxt1m.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().generate("ltepu4mm0qzw6lkfxt1m.jpg")!, cloudinary: cloudinary)
Image delivery

Below, the suffix basketball-game-in-college was added, which is the text that search engines use to index the page and image:

Ruby:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", :url_suffix=>"basketball-game-in-college")
PHP v1:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", array("url_suffix"=>"basketball-game-in-college"))
PHP v2:
Copy to clipboard
(new ImageTag('ltepu4mm0qzw6lkfxt1m.jpg'))
  ->suffix('basketball-game-in-college');
Python:
Copy to clipboard
CloudinaryImage("ltepu4mm0qzw6lkfxt1m.jpg").image(url_suffix="basketball-game-in-college")
Node.js:
Copy to clipboard
cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {url_suffix: "basketball-game-in-college"})
Java:
Copy to clipboard
cloudinary.url().suffix("basketball-game-in-college").imageTag("ltepu4mm0qzw6lkfxt1m.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('ltepu4mm0qzw6lkfxt1m.jpg', {urlSuffix: "basketball-game-in-college"}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {url_suffix: "basketball-game-in-college"})
React:
Copy to clipboard
<Image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="basketball-game-in-college">

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="basketball-game-in-college">

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="ltepu4mm0qzw6lkfxt1m.jpg" url-suffix="basketball-game-in-college">

</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Suffix("basketball-game-in-college").BuildImageTag("ltepu4mm0qzw6lkfxt1m.jpg")
Android:
Copy to clipboard
MediaManager.get().url().suffix("basketball-game-in-college").generate("ltepu4mm0qzw6lkfxt1m.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setSuffix( "basketball-game-in-college").generate("ltepu4mm0qzw6lkfxt1m.jpg")!, cloudinary: cloudinary)

In the URL below, the same image is given an additional, separate suffix in Spanish:

Ruby:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", :url_suffix=>"baloncesto-juego-en-universidad")
PHP v1:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", array("url_suffix"=>"baloncesto-juego-en-universidad"))
PHP v2:
Copy to clipboard
(new ImageTag('ltepu4mm0qzw6lkfxt1m.jpg'))
  ->suffix('baloncesto-juego-en-universidad');
Python:
Copy to clipboard
CloudinaryImage("ltepu4mm0qzw6lkfxt1m.jpg").image(url_suffix="baloncesto-juego-en-universidad")
Node.js:
Copy to clipboard
cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {url_suffix: "baloncesto-juego-en-universidad"})
Java:
Copy to clipboard
cloudinary.url().suffix("baloncesto-juego-en-universidad").imageTag("ltepu4mm0qzw6lkfxt1m.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('ltepu4mm0qzw6lkfxt1m.jpg', {urlSuffix: "baloncesto-juego-en-universidad"}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {url_suffix: "baloncesto-juego-en-universidad"})
React:
Copy to clipboard
<Image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="baloncesto-juego-en-universidad">

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="baloncesto-juego-en-universidad">

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="ltepu4mm0qzw6lkfxt1m.jpg" url-suffix="baloncesto-juego-en-universidad">

</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Suffix("baloncesto-juego-en-universidad").BuildImageTag("ltepu4mm0qzw6lkfxt1m.jpg")
Android:
Copy to clipboard
MediaManager.get().url().suffix("baloncesto-juego-en-universidad").generate("ltepu4mm0qzw6lkfxt1m.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setSuffix( "baloncesto-juego-en-universidad").generate("ltepu4mm0qzw6lkfxt1m.jpg")!, cloudinary: cloudinary)

Additional image transformations can be easily made by adding parameters to Cloudinary's on-the-fly manipulation URLs. Here, the same image is transformed to a 200 x 200 pixel crop with rounded corners and increased saturation:

Ruby:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", :width=>200, :height=>200, :gravity=>"west", :radius=>30, :effect=>"saturation:50", :crop=>"fill", :url_suffix=>"basketball-game-in-college")
PHP v1:
Copy to clipboard
cl_image_tag("ltepu4mm0qzw6lkfxt1m.jpg", array("width"=>200, "height"=>200, "gravity"=>"west", "radius"=>30, "effect"=>"saturation:50", "crop"=>"fill", "url_suffix"=>"basketball-game-in-college"))
PHP v2:
Copy to clipboard
(new ImageTag('ltepu4mm0qzw6lkfxt1m.jpg'))
  ->resize(Resize::fill()->width(200)->height(200)->gravity(Gravity::compass(Compass::west())))
  ->roundCorners(RoundCorners::byRadius(30))
  ->adjust(Adjust::saturation()->level(50))
  ->suffix('basketball-game-in-college');
Python:
Copy to clipboard
CloudinaryImage("ltepu4mm0qzw6lkfxt1m.jpg").image(width=200, height=200, gravity="west", radius=30, effect="saturation:50", crop="fill", url_suffix="basketball-game-in-college")
Node.js:
Copy to clipboard
cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {width: 200, height: 200, gravity: "west", radius: 30, effect: "saturation:50", crop: "fill", url_suffix: "basketball-game-in-college"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().width(200).height(200).gravity("west").radius(30).effect("saturation:50").crop("fill")).suffix("basketball-game-in-college").imageTag("ltepu4mm0qzw6lkfxt1m.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('ltepu4mm0qzw6lkfxt1m.jpg', {width: 200, height: 200, gravity: "west", radius: 30, effect: "saturation:50", crop: "fill", urlSuffix: "basketball-game-in-college"}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("ltepu4mm0qzw6lkfxt1m.jpg", {width: 200, height: 200, gravity: "west", radius: 30, effect: "saturation:50", crop: "fill", url_suffix: "basketball-game-in-college"})
React:
Copy to clipboard
<Image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="basketball-game-in-college">
  <Transformation width="200" height="200" gravity="west" radius="30" effect="saturation:50" crop="fill" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="ltepu4mm0qzw6lkfxt1m.jpg" urlSuffix="basketball-game-in-college">
  <cld-transformation width="200" height="200" gravity="west" radius="30" effect="saturation:50" crop="fill" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="ltepu4mm0qzw6lkfxt1m.jpg" url-suffix="basketball-game-in-college">
  <cl-transformation width="200" height="200" gravity="west" radius="30" effect="saturation:50" crop="fill">
  </cl-transformation>
</cl-image>
.NET:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Width(200).Height(200).Gravity("west").Radius(30).Effect("saturation:50").Crop("fill")).Suffix("basketball-game-in-college").BuildImageTag("ltepu4mm0qzw6lkfxt1m.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().width(200).height(200).gravity("west").radius(30).effect("saturation:50").crop("fill")).suffix("basketball-game-in-college").generate("ltepu4mm0qzw6lkfxt1m.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setSuffix( "basketball-game-in-college").setTransformation(CLDTransformation().setWidth(200).setHeight(200).setGravity("west").setRadius(30).setEffect("saturation:50").setCrop("fill")).generate("ltepu4mm0qzw6lkfxt1m.jpg")!, cloudinary: cloudinary)
200x200 thumbnail with dynamic SEO suffix

This capability is also applicable for private images and non-image raw file uploads. For raw files, the resource type /raw/upload should be replaced by /files, and for private images, the resource type and type /image/private should be replaced by /private_images. When using Cloudinary’s SDK for various development frameworks, set the new url_suffix parameter to any text, and the URLs will be built automatically with either a /files or /private_imagesprefix, as well as the added suffix.

Use Your Own Domain

You can also make your URLs more SEO friendly by using a custom domain (CNAME) for your URLs instead of the shared res.cloudinary.com. The SEO suffix and CNAME features can also be used together, for example:

http://images.<mydomain.com>/w_200/afe6c8e2ca/basktetball-game.jpg

Note that in order to use the SEO suffix or CNAME features, your account needs to be setup with a private CDN configuration which is only supported for the Advanced plan or higher. We invite you to contact us to setup these advanced features.

Update (27/02/2017): The dynamic SEO suffix feature is now available for all plans and no longer requires a private CDN configuration.

Summary

With these two capabilities, Cloudinary aims to help you easily create advanced image manipulation and delivery URLs to better optimize your site for search engines. Cloudinary users can use both the Root Path URLs and Dynamic SEO Suffix features together to build a short and descriptive image URL. The Root Path URL capability is available for all accounts, including the free tier, and the Dynamic SEO Suffix capability is available with Cloudinary’s Advanced Plan or higher with a private CDN setup.

If you don't have a Cloudinary account yet, we invite you sign up for a free account here.

Recent Blog Posts

Generate Waveform Images from Audio with Cloudinary

This is a reposting of an article written by David Walsh. Check out his blog HERE!
I've been working a lot with visualizations lately, which is a far cry from your normal webpage element interaction coding; you need advanced geometry knowledge, render and performance knowledge, and much more. It's been a great learning experience but it can be challenging and isn't always an interest of all web developers. That's why we use apps and services specializing in complex tasks like Cloudinary: we need it done quickly and by a tool written by an expert.

Read more
Make All Images on Your Website Responsive in 3 Easy Steps

Images are crucial to website performance, but most still don't implement responsive images. It’s not just about fitting an image on the screen, but also making the the image size relatively rational to the device. The srcset and sizes options, which are your best hit are hard to implement. Cloudinary provides an easier way, which we will discuss in this article.

Read more

The Future of Audio and Video on the Web

By Prosper Otemuyiwa
The Future of Audio and Video on the Web

Web sites and platforms are becoming increasingly media-rich. Today, approximately 62 percent of internet traffic is made up of images, with audio and video constituting a growing percentage of the bytes.

Read more

Embed Images in Email Campaigns at Scale

By Sourav Kundu
Embed Images in Email Campaigns at Scale

tl;dr

Cloudinary is a powerful image hosting solution for email marketing campaigns of any size. With features such as advanced image optimization and on-the-fly image transformation, backed by a global CDN, Cloudinary provides the base for a seamless user experience in your email campaigns leading to increased conversion and performance.

Read more
Build the Back-End For Your Own Instagram-style App with Cloudinary

Github Repo

Managing media files (processing, storage and manipulation) is one of the biggest challenges we encounter as practical developers. These challenges include:

A great service called Cloudinary can help us overcome many of these challenges. Together with Cloudinary, let's work on solutions to these challenges and hopefully have a simpler mental model towards media management.

Read more

Build A Miniflix in 10 Minutes

By Prosper Otemuyiwa
Build A Miniflix in 10 Minutes

Developers are constantly faced with challenges of building complex products every single day. And there are constraints on the time needed to build out the features of these products.

Engineering and Product managers want to beat deadlines for projects daily. CEOs want to roll out new products as fast as possible. Entrepreneurs need their MVPs like yesterday. With this in mind, what should developers do?

Read more