Building a delivery URL or an <img> tag for an image already in Cloudinary.
Transformations are applied by the CDN at delivery time — nothing is re-uploaded, and the
original is never modified.
<?php
require 'vendor/autoload.php';
use Cloudinary\Cloudinary;
use Cloudinary\Transformation\Delivery;
use Cloudinary\Transformation\Format;
use Cloudinary\Transformation\Gravity;
use Cloudinary\Transformation\Quality;
use Cloudinary\Transformation\Resize;
$cloudinary = new Cloudinary();
$url = $cloudinary->image('sample')
->resize(Resize::fill(400, 400)->gravity(Gravity::auto()))
->delivery(Delivery::format(Format::auto()))
->delivery(Delivery::quality(Quality::auto()));
echo $url, PHP_EOL;Output:
https://res.cloudinary.com/<cloud_name>/image/upload/c_fill,g_auto,h_400,w_400/f_auto/q_auto/sample?_a=BAAHWXGY
Runnable version: examples/transform-and-deliver-image.php.
Generated URLs carry an _a query parameter — anonymous SDK-version telemetry, no
account or asset data. It does not affect delivery or caching. The examples below omit it
for readability; real output always includes it. To turn it off:
$cloudinary = new Cloudinary('cloudinary://key:secret@cloud?analytics=false');The builder is not a response object — it produces a string. Cast it explicitly when you need one:
$url = (string) $cloudinary->image('sample')->resize(Resize::scale(300));In string context — echo, interpolation, concatenation — the cast is automatic.
Format::auto() serves AVIF or WebP to browsers that accept them; Quality::auto()
picks a compression level per image. Together they are the single biggest byte saving
available, with no visible quality loss in most cases.
These use additional classes from the same namespace:
use Cloudinary\Transformation\Background;
use Cloudinary\Transformation\Effect;// Crop to a square, keeping the most interesting region.
$cloudinary->image('sample')->resize(Resize::fill(400, 400)->gravity(Gravity::auto()));
// Scale to a width, preserving aspect ratio.
$cloudinary->image('sample')->resize(Resize::scale(300));
// Crop to a face.
$cloudinary->image('sample')->resize(Resize::thumbnail(150, 150)->gravity(Gravity::face()));
// Extend an image to a new aspect ratio with generated content.
$cloudinary->image('sample')->resize(Resize::pad(800, 800)->background(Background::generativeFill()));
// Effects and shapes chain in the order you write them.
$cloudinary->image('sample')
->resize(Resize::fill(200, 200))
->effect(Effect::grayscale());Each call maps to one component of the URL, so the generated path is predictable:
c_fill,h_200,w_200/e_grayscale/sample.
$tag = $cloudinary->imageTag('sample')->resize(Resize::fill(400, 400));
echo $tag, PHP_EOL;
// <img src="https://res.cloudinary.com/<cloud_name>/image/upload/c_fill,h_400,w_400/sample">A delivery URL needs no credentials — it is meant to be put in HTML. Restricting access is a separate feature; see access control.
When a public_id contains a slash and no version is known, the SDK inserts a v1
placeholder:
$cloudinary->image('sample'); // .../image/upload/sample
$cloudinary->image('folder/sub/sample'); // .../image/upload/v1/folder/sub/sampleThis is expected and the URL resolves correctly. To emit a real version, pass the
version from the upload response.
| Symptom | Cause |
|---|---|
| URL returns 404 | The public_id is wrong, or the asset is a different resource_type. Check resource_type and folder path. |
URL contains v1 unexpectedly |
Normal for nested public IDs — see above. |
| Transformation ignored | Component order matters; verify against the generated URL rather than the code. |
| Image is larger than expected | Add Delivery::format(Format::auto()) and Delivery::quality(Quality::auto()). |
- Transform and deliver a video
- Upload an image
- Troubleshoot errors
- Transformation reference — every parameter, with the URL syntax each one produces.
- Transformation builder skill
— install it (
npx skills add cloudinary-devs/skills) rather than guessing at transformation chains.