Skip to content

[Bug]: Email viewer preview stuck at ~150px placeholder height until unreachable remote image times out (~60s) #635

Description

@Technophile-services

Description

Version: v1.7.6 (also reproduced on v1.7.4, v1.7.5)
Browser: Firefox 152.0 (also reproduced on Chrome)
Deployment: OpenShift (container: ghcr.io/bulwarkmail/webmail:latest)

When opening an email that contains an inline remote image (e.g. a sender logo or tracking pixel) whose server is unreachable or slow, the email viewer preview window gets stuck at a placeholder height of approximately 150px. The email body does not render until the browser's TCP connection timeout fires (~60 seconds), at which point the image request fails with a network error and the full email appears.

Root Cause Analysis

Since v1.6.7, the email viewer sandboxes email HTML inside a srcDoc iframe with a CSP meta tag for security. The render pipeline appears to wait for the iframe's onload event or a Promise.all over image resource loads before marking the preview as complete and expanding the container from its placeholder height. When an image fetch hangs indefinitely, that signal never fires.

Network Evidence

Below is a captured hanging request from Firefox's Network tab (redacted):

GET /LogoTechnophile-72dpi.png
Host: technophile.services
Status: (pending) -- stalled for ~60s until timeout
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-site

The request hangs for ~60 seconds before failing. The email viewer renders immediately after the failure.

Related Changelog Entries

v1.6.7 -- "Sandbox thread email HTML in srcDoc iframe with a CSP tag"
v1.6.3 -- "Redesign external content prompt"
v1.6.0 -- "Respect per-email dark mode toggle when 'always show in light mode' is on"
v1.7.4 -- "Correct dark-mode background-image inversion and height clipping in the email viewer"

Suggested Fix

The email viewer should render the email body independently of external resource loading. Options include:

Fire render-complete on DOMContentLoaded, not on window.load -- This is the most correct fix. The iframe content is available immediately; images should load asynchronously.

Add loading="lazy" to external img elements -- Native lazy loading defers image fetches so they don't block the initial render.

Per-image timeout with fallback -- Set a client-side AbortController timeout of e.g. 10s per image, and hide or replace the broken img with a placeholder on failure.

Always block remote images by default in the sandbox iframe -- Only load images when the user explicitly clicks "Load images" or "Trust sender". This is already the privacy-best practice. 

Workaround

In Settings -> Mail, enable "Block remote images" or "Block external content". When remote images are blocked, the preview renders immediately. Trusted senders can be whitelisted per-email via the "Load images" button, and a single failed image there only affects that img tag, not the entire viewer.

Steps to Reproduce

Log into Bulwark Webmail.
Open an email that contains an tag pointing to an external URL that is either unreachable (server down, DNS failure) or very slow to respond.
Observe the preview pane: it stays at ~150px height with a loading/skeleton state.
Open DevTools -> Network tab.
Observe the image request stuck in "pending" state.
After ~60 seconds (browser timeout), the request fails and the email body renders fully.

Expected Behavior

The email preview should render immediately. External images should load asynchronously in the background. A single hanging image must not block the entire email viewer from displaying.

Actual Behavior

The email viewer holds the render-complete signal until all embedded resources have either loaded or failed. An image that hangs (server accepts TCP connection but never sends data or closes the connection) blocks the entire preview for the duration of the browser's connection timeout (~60 seconds).

Bulwark Version

1.7.6

Stalwart Mail Server Version

latest

Browser

Firefox

Operating System

Linux

Screenshots / Screen Recording

No response

Relevant Logs or Error Output

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions