How to Preserve File Names with sorl-thumbnail for Better Image SEO

Blog / Django · September 18, 2014 · Updated June 10, 2026 · 6 min read
How to Preserve File Names with sorl-thumbnail for Better Image SEO

By default, sorl-thumbnail saves every generated thumbnail under a hashed cache path (something like cache/a1/b2/a1b2c3d4e5f6....jpg). That is perfect for fast, collision-free cache lookups, but the filename carries zero meaning for search engines. To keep human-readable, keyword-rich thumbnail filenames you do two things: (1) override the thumbnail backend with a custom THUMBNAIL_BACKEND subclass that overrides _get_thumbnail_filename(), and (2) make sure the original upload already has a slugified name via your model's upload_to callable.

Be honest about the payoff, though: the image filename is a minor ranking signal. It helps far less than descriptive alt text, the surrounding page context, and Core Web Vitals. Treat readable filenames as one small part of a complete image-SEO setup, not the headline.

Key takeaways

  • sorl-thumbnail's default cache filenames are hashed on purpose (cache keys), so they are fine for performance but useless for SEO.
  • Subclass ThumbnailBackend and override _get_thumbnail_filename() to emit <prefix>/<source-key>/<slugified-name>.<ext> — readable, yet still collision-safe.
  • Always keep the source key as a folder so two different images that share a base name never overwrite each other.
  • Fix the original filename too, with a slugifying upload_to callable on the model field — the thumbnail name is derived from it.
  • Filename is a low-weight signal; alt text, page context, and Core Web Vitals matter far more, so ship the whole checklist.

Why are sorl-thumbnail's default filenames bad for SEO?

When you call {% thumbnail %} or get_thumbnail(), sorl computes a cache key from the source image, the geometry string, and the options, then writes the result into the THUMBNAIL_PREFIX directory under that hashed key. The hash guarantees a unique path per (image + size + options) combination, so caching and invalidation just work.

The downside is that a thumbnail of red-leather-handbag.jpg ends up as something like cache/9f/2c/9f2c....webp. Google can still index it, but you have thrown away a free, descriptive token. A readable filename such as red-leather-handbag.webp reinforces what the image is — a small but real win, especially in Google Images.

How do you give the original upload an SEO-friendly name?

The thumbnail name is derived from the source filename, so start at the source. Pass an upload_to callable to your ImageField that slugifies the incoming filename. This also strips spaces, accents, and IMG_4821.JPG-style camera junk before anything is stored.

# models.py
import os

from django.db import models
from django.utils.text import slugify


def seo_upload_to(instance, filename):
    """Store uploads as products/<slugified-base>.<ext>."""
    base, ext = os.path.splitext(filename)
    return f"products/{slugify(base)}{ext.lower()}"


class Product(models.Model):
    name = models.CharField(max_length=200)
    image = models.ImageField(upload_to=seo_upload_to)

How do you make sorl-thumbnail keep readable thumbnail filenames?

There is still no built-in setting to preserve filenames, so you override the backend. Point THUMBNAIL_BACKEND at your own subclass in settings.py:

# settings.py
THUMBNAIL_BACKEND = "myapp.thumbnail_backends.SEOThumbnailBackend"

Then create the subclass and override _get_thumbnail_filename(). The key trick is to keep the original cache key as a folder and append a slugified version of the source name as the actual file. You get readable filenames without ever risking a collision between two different images that happen to share a base name.

# myapp/thumbnail_backends.py
import os.path

from django.utils.text import slugify
from sorl.thumbnail.base import ThumbnailBackend, EXTENSIONS
from sorl.thumbnail.conf import settings
from sorl.thumbnail.helpers import tokey, serialize


class SEOThumbnailBackend(ThumbnailBackend):
    """Store thumbnails as <prefix>/<source-key>/<slugified-name>.<ext>."""

    def _get_thumbnail_filename(self, source, geometry_string, options):
        # Unique cache key per (image + geometry + options).
        key = tokey(source.key, geometry_string, serialize(options))

        base, _ext = os.path.splitext(os.path.basename(source.name))
        # Keep the key as a folder so same-named images never collide;
        # fall back to the key if the slug comes out empty.
        name = slugify(base) or key
        path = f"{key}/{name}"
        return f"{settings.THUMBNAIL_PREFIX}{path}.{EXTENSIONS[options['format']]}"

Example: an upload of micropyramid-logo.png now produces a 200x200 WebP thumbnail at THUMBNAIL_PREFIX/<source-key>/micropyramid-logo.webp instead of a fully hashed path. The folder is unique per cache key, so a different micropyramid-logo.png uploaded elsewhere lands in its own folder — no overwrite. After changing the backend, clear the existing cache so old hashed entries are regenerated with the new naming.

Which image-SEO factors actually matter in 2026?

Filenames are the easy thing to obsess over, but they are near the bottom of the list. Here is the honest priority order and how to implement each in a Django + sorl-thumbnail stack.

Image-SEO factor Ranking impact How to do it in Django / sorl-thumbnail
Descriptive alt text High Set a meaningful alt on every <img>; store it on the model, never leave it blank or stuff keywords
Page context & surrounding copy High Place images near relevant headings/captions; use semantic HTML
Core Web Vitals (LCP, CLS) High Set explicit width/height, lazy-load below the fold, preload the LCP image
Responsive srcset / sizes Medium-High Render several {% thumbnail %} widths and build a srcset
Modern formats (WebP / AVIF) Medium-High get_thumbnail(img, '800', format='WEBP') or set THUMBNAIL_FORMAT
Correct dimensions (no upscaling) Medium Use geometry strings; sorl will not upscale past the source by default
Image sitemap Medium Add image entries to your sitemap.xml so crawlers discover them
Lazy loading Low-Medium Add loading="lazy" (and decoding="async") to non-critical images
Descriptive filename Low (minor) Custom THUMBNAIL_BACKEND + slugified upload_to (this post)

How do filenames fit into the bigger image-SEO picture?

Readable filenames are a finishing touch on top of the work that moves the needle. If you are still wiring up sorl-thumbnail, start with our guide on generating thumbnails in Django with sorl-thumbnail, then add multiple sizes with responsive thumbnails using srcset and sizes. Once your media is performance-critical, serve it from the edge — see Amazon S3 and CloudFront CDN for faster loading.

MicroPyramid has built and maintained Django applications for 12+ years across 50+ projects, and image pipelines like this are routine for us. If you want a second pair of eyes on your media stack, our Django development services team can help.

Frequently Asked Questions

Does the image filename affect Google rankings?

Only slightly. Google has said the filename is one of several signals it uses to understand an image, alongside alt text, captions, structured data, and the page it sits on. A descriptive name like red-leather-handbag.webp is better than IMG_4821.jpg, but it will never outweigh good alt text and relevant page context. Treat it as a low-effort, low-weight win.

Why does sorl-thumbnail hash thumbnail filenames by default?

The hash is a deterministic cache key built from the source image, the geometry string, and the options. It guarantees a unique path for every variant, makes cache invalidation trivial, and prevents two different requests from clobbering each other's files. The trade-off is that the name is meaningless to search engines — which is exactly what the custom backend in this post fixes.

Will keeping the same filename for different images cause collisions?

No, as long as you keep the source key as a folder. In the SEOThumbnailBackend above, each thumbnail is written to <prefix>/<key>/<slug>.<ext>, where <key> is unique per image-and-options combination. Two different products both named logo.png therefore land in separate folders. Never drop the key and write straight into a flat directory — that is when collisions happen.

Is alt text or the filename more important for image SEO?

alt text wins, clearly. It is read by screen readers (accessibility), used by Google to understand the image, and shown when the image fails to load. Write a concise, natural description of what the image shows. The filename is a secondary, supporting signal — useful, but do the alt text first.

How do I serve WebP or AVIF thumbnails with sorl-thumbnail?

Pass a format to get_thumbnail() (for example format='WEBP') or set THUMBNAIL_FORMAT in settings, provided your Pillow build supports the format. WebP is universally supported in modern browsers; AVIF is well supported but compresses slower. A common pattern is a <picture> element offering AVIF, then WebP, then a JPEG fallback, with each source generated by a {% thumbnail %} call.

Do I still need an image sitemap?

It helps, especially for image-heavy sites where pictures load lazily or via JavaScript and might be missed during crawling. Add image entries to your existing sitemap.xml so Google can discover and index them. It will not magically rank images on its own, but it removes a discovery bottleneck — a worthwhile addition once your filenames, alt text, and formats are sorted.

Share this article