Backend
Cloudinary: Gestió d'Imatges a Producció sense Maldecaps
Com pujar, optimitzar i servir imatges de manera professional amb Cloudinary. Tutorial pràctic amb exemples reals de Next.js i React.
Publicat el 22 d’abril del 2026
Per què no pots servir imatges des del teu servidor
Quan comences un projecte, el primer instint és guardar les imatges al public/ del teu projecte o directament al servidor. Funciona... fins que no funciona:
- Pes: Una foto de mòbil pesa 3-8MB. Multiplica per 50 receptes amb 3 fotos cadascuna i tens 1GB+ de dades servides directament des del teu hosting.
- Format: Els navegadors moderns suporten WebP i AVIF, que pesen un 40-60% menys que JPEG. Però no tots els navegadors ho suporten igual.
- Mides: Serves la mateixa imatge de 4000px d'ample per a un mòbil de 375px? Estàs malbaratant ample de banda.
- CDN: Si el teu servidor està a Europa i l'usuari a Amèrica, cada imatge triga més del necessari.
Cloudinary resol tot això amb una sola URL.
Què és Cloudinary?
Cloudinary és un servei de gestió de media al núvol. En termes pràctics:
- Puges la imatge original (una sola vegada)
- Transformes via URL: mida, qualitat, format, retall...
- Serves des d'un CDN global automàticament
El pla gratuït inclou 25GB de storage i 25GB de bandwidth mensual — suficient per a projectes petits i mitjans.
Configuració Inicial
Necessites 3 valors del teu dashboard de Cloudinary:
CLOUDINARY_CLOUD_NAME=el-teu-cloud
CLOUDINARY_API_KEY=123456789
CLOUDINARY_API_SECRET=abc-secret
Important: L'API Secret NOMÉS va al servidor. Mai l'exposis al frontend.
Instal·la el SDK:
npm install cloudinary
Configura'l al backend:
import { v2 as cloudinary } from 'cloudinary'
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
Pujar Imatges: El Flux Complet
1. Frontend: Capturar l'arxiu
El formulari envia l'arxiu com a FormData:
const handleUpload = async (file) => {
const formData = new FormData()
formData.append('image', file)
const res = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const data = await res.json()
// data.url conté la URL de Cloudinary
}
2. Backend: Pujar a Cloudinary
A l'API route del teu servidor (Next.js exemple):
const result = await cloudinary.uploader.upload(filePath, {
folder: 'receptes',
quality: 'auto',
format: 'auto',
})
// result.secure_url → la URL pública
// result.public_id → l'identificador per transformar
Els paràmetres clau:
folder: organitza les imatges per carpetes (receptes/,avatars/, etc.)quality: 'auto': Cloudinary analitza la imatge i tria la qualitat òptimaformat: 'auto': serveix WebP als navegadors que ho suporten, JPEG als altres
3. Guardar a Base de Dades
Guarda el public_id i la secure_url:
{
"url": "result.secure_url",
"publicId": "result.public_id",
"width": "result.width",
"height": "result.height"
}
Transformacions via URL: El Poder Real
Un cop tens la imatge pujada, pots transformar-la simplement canviant la URL.
https://res.cloudinary.com/CLOUD/image/upload/TRANSFORMACIONS/PUBLIC_ID.FORMAT
Exemples pràctics:
# Redimensionar a 800px d'ample
/w_800,c_limit/receptes/pasta-carbonara.jpg
# Thumbnail quadrat 200x200
/w_200,h_200,c_fill,g_auto/receptes/pasta-carbonara.jpg
# Qualitat + format automàtic
/q_auto,f_auto/receptes/pasta-carbonara.jpg
Transformacions més útils:
w_X— Ample màximc_fill— Retalla per omplir les dimensions exactesg_auto— Detecció automàtica del punt d'interèsq_auto— Qualitat optimitzada automàticamentf_auto— Format modern automàtic (WebP/AVIF)
Patró Pràctic: Helper de URLs
const cloudinaryUrl = (publicId, options = {}) => {
const {
width = 800,
height,
crop = 'limit',
quality = 'auto',
format = 'auto',
} = options
const transforms = [
`w_${width}`,
height ? `h_${height}` : '',
`c_${crop}`,
`q_${quality}`,
`f_${format}`,
].filter(Boolean).join(',')
const cloud = process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME
return `https://res.cloudinary.com/${cloud}/image/upload/${transforms}/${publicId}`
}
Ús:
// Card de recepta
cloudinaryUrl('receptes/pasta', { width: 400, height: 300, crop: 'fill' })
// Detall de recepta
cloudinaryUrl('receptes/pasta', { width: 1200 })
// Avatar petit
cloudinaryUrl('avatars/user123', { width: 80, height: 80, crop: 'fill' })
Esborrar Imatges
await cloudinary.uploader.destroy(publicId)
Guarda sempre el publicId a la base de dades. Sense ell no pots esborrar la imatge.
Optimització Real: Quant Estalvies?
Dades reals d'un projecte amb ~200 imatges:
- Sense optimització: 1.2GB transferits/mes
- Amb
q_auto + f_auto: 480MB transferits/mes (60% menys) - Amb redimensionat adaptatiu: 280MB transferits/mes (77% menys)
Errors Comuns
1. Exposar l'API Secret al frontend. L'API Secret permet esborrar qualsevol imatge del teu compte. Sempre puja des del servidor.
2. No esborrar imatges antigues. Si l'usuari canvia l'avatar 10 vegades, tens 10 imatges ocupant espai. Esborra l'antiga abans de pujar la nova.
3. No posar límits de mida. Valida la mida del fitxer ABANS de pujar-lo:
if (file.size > 10 * 1024 * 1024) {
return res.status(400).json({ error: 'Imatge massa gran (màx 10MB)' })
}
4. Servir sempre la mateixa mida. No serveixis una imatge de 2000px per a un thumbnail de 100px.
5. No usar f_auto. Si servixes JPEG a un navegador que suporta WebP, estàs enviant un 40% més de dades del necessari.
Resum
- Puja una sola vegada la imatge original a Cloudinary
- Transforma via URL per a cada cas d'ús
- Usa
q_auto,f_autosempre per optimització automàtica - Guarda el
publicIda la base de dades, no només la URL - Esborra imatges quan ja no es necessiten
- Valida al servidor la mida i tipus d'arxiu abans de pujar
- Mai exposis l'API Secret al frontend