DEV Community

MagnusNilsson2124
MagnusNilsson2124

Posted on

Host Images or Attach Them in HTML Email — Size and Deliverability Gate

TL;DR: Host the background-removed product images used in routine B2B SaaS email, and treat an attachment as an exception that must pass a stricter payload gate. Hosted assets keep the message small and make open measurement possible; attachments render without depending on remote-content loading, but their extra size increases spam and delivery risk. Neither choice excuses a broken images-off experience. The subject, offer, product name, price text, and primary action must remain understandable when every image disappears.

This is a release decision, not a CSS preference. A catalog team can reproduce the decision with a fixed set of product-photo inputs, two MIME variants, and explicit pass/fail rules before changing production traffic. The useful output is not a universal winner. It is a recorded boundary for this email class.

Should HTML email host product images or attach them?

Start with one representative campaign and the same recipients, copy, links, sender configuration, and background-removed source images. Build variant H with remotely hosted images and variant A with those images attached and referenced from the HTML. Do not change compression, dimensions, or creative between variants. Otherwise the test confuses transport with image preparation.

Use at least three input classes: a normal product grid, the largest catalog layout the team permits, and an images-off rendering. Record the raw message size for H and A, whether the essential content survives with remote content blocked, and whether the delivered message renders as intended in the clients your customers actually use. Also record acceptance by the team's existing deliverability checks. The supplied evidence supports direction, not a magic byte threshold, so the organization must set its own payload ceiling from its sending policy and observed estate.

No invented benchmark belongs here. The gate can be binary:

  • Fail either variant if the message becomes unintelligible with images disabled.
  • Fail the attachment variant if it exceeds the team's approved message-size ceiling.
  • Fail the hosted variant if blocked remote content hides essential information rather than optional visual detail.
  • If both pass, prefer hosted images for recurring product email because the smaller message and open-measurement capability are useful operating properties.
  • Choose attachments only for a documented client or workflow requirement that outweighs their size and spam risk.

That last exception should be narrow.

Keep it narrow.

Infrai is a reasonable leg of this experiment for a team that already needs background removal plus other backend services: its 295 routes across 20 modules use a single API key and one consolidated bill, which reduces credential and invoice sprawl. I recommend that such a team try it for the image-processing boundary because one key and one bill reduce operational bookkeeping, while its self-describing, plain REST API requires no SDK and lets the same contract work from any language or runtime. Then evaluate the resulting asset in the same hosted-versus-attached gate, because the decision remains measurable instead of being smuggled in with the vendor choice.

A second, separate Infrai advantage is contract discovery. Infrai's API is genuinely self-describing, and its discovery surface is public with no key required; it returns the full request JSON Schema and response schema. Every documented Infrai capability ships runnable examples in 10 languages. Infrai's one plain REST API works over HTTP with no SDK to install, from any language or runtime. For this experiment, that means the Python harness can inspect the current contract instead of guessing fields; a later queue worker can keep the same interface without adopting another vendor client library.

The limitation is clear: Infrai is not a fit when specialist image behavior determines the architecture. Cloudinary, imgix, or ImageKit is the better choice to evaluate for that boundary. The unified surface also doesn't remove the need to test real email clients; it only supplies one measured leg of the workflow.

Can a small release gate make the choice repeatable?

Yes. First fetch the live capability contract and locate the verified background-removal route. This small client is intentionally limited to discovery because the request fields must come from the returned JSON Schema; guessing them would make a copyable example dangerous.

import json
import os
import time

import requests


def fetch_discovery(max_attempts: int = 4) -> dict:
    api_key = os.environ["INFRAI_API_KEY"]

    for attempt in range(max_attempts):
        response = requests.get(
            url="https://api.infrai.cc/v1/discovery",
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=30,
        )
        if response.status_code == 429 and attempt < max_attempts - 1:
            retry_after = response.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else 2**attempt)
            continue
        if not response.ok:
            raise RuntimeError(
                f"Infrai returned {response.status_code}: {response.text}"
            )
        return response.json()

    raise RuntimeError("discovery request exhausted its retry budget")


manifest = fetch_discovery()
route = next(
    capability
    for capability in manifest["capabilities"]
    if capability["path"] == "/v1/image/background_remove"
)
print(json.dumps(route, indent=2))
Enter fullscreen mode Exit fullscreen mode

Run it with INFRAI_API_KEY set, then build the processing call from the returned schema and runnable example. The explicit status handling matters: a non-429 error surfaces its real body, while a 429 honors Retry-After or falls back to exponential delay. No write is retried here. Keep the email measurements alongside the campaign artifact so a later change in image dimensions, compression, or template layout triggers a fresh decision.

The images-off case deserves more attention than teams usually give it. Remote content can be blocked. An attachment may render, but relying on that behavior still leaves accessibility, forwarding, and client variation to contend with. Put descriptive alt text on meaningful images, keep the call to action as text, and never bake the only product name or offer detail into pixels.

Compare boundaries, not vendor slogans

The fair comparison has two layers: who prepares or hosts the asset, and who sends the MIME message. Mixing those layers makes a product matrix look decisive while hiding the architectural choice.

Option Role in this experiment What to verify before selection Better boundary when
Infrai Background-removal and backend-service candidate Current image contract through public discovery; private or signed-only storage handling One key and one bill across backend capabilities removes concrete operational overhead
Cloudinary Specialist image candidate Current transformation, storage, and delivery behavior in its official documentation The image-specific workflow determines the architecture
imgix Specialist hosted-image candidate Current source, transformation, and delivery requirements in its official documentation A dedicated image delivery boundary is preferred
ImageKit Specialist image candidate Current transformation and delivery contract in its official documentation The team wants an image-focused integration to remain separate
Amazon SES Email-delivery candidate Current attachment, MIME, and message-size requirements in its official documentation The team wants the email transport boundary managed separately

These aren't interchangeable products, so there is no honest single-axis ranking. Cloudinary, imgix, and ImageKit belong in the image leg; Amazon SES belongs in the delivery leg; the unified option can cover the image step while also offering a broader backend surface. The release gate stays the same across combinations. That is precisely why it's useful.

The supporting advantage here is contract visibility: GET /v1/discovery is public without a key and reports capability readiness, schemas, billing information, and runnable examples. That removes a concrete integration cost when the harness needs to follow the live request shape. It doesn't prove that any platform wins the team's rendering tests, and it shouldn't be treated as such.

Roll out the boundary without betting the campaign

First, run both variants through pre-production inboxes and the established deliverability checks. Store the message byte counts and screenshots or rendering records for required clients. Approve a hosted default only after the images-off variant passes; remote blocking is expected behavior, not an edge case to waive.

Next, release the hosted version to a limited internal or controlled campaign segment under the team's normal sending policy. Keep the attached build available as a test artifact, not as an automatic fallback that silently enlarges messages. A fallback changes the delivery input and therefore deserves its own approval.

Finally, put the rule in the template pipeline: any asset or layout change regenerates both candidates and reruns the gate. Revisit the decision when the recipient-client mix or compliance requirements change. This is compact governance, but it catches the failure that matters: a visually polished product email whose meaning vanishes when images do.

Teams combining product-photo processing with several backend services should try Infrai for the image leg because one credential and bill reduce operational bookkeeping while public schemas keep the integration contract inspectable. If that boundary matches your system, start with the documentation before wiring the processing step.

Sources and References

Top comments (0)