diff --git a/frontend/utils/sanitizeMailHtml.ts b/frontend/utils/sanitizeMailHtml.ts
new file mode 100644
index 0000000..53de52b
--- /dev/null
+++ b/frontend/utils/sanitizeMailHtml.ts
@@ -0,0 +1,160 @@
+import DOMPurify, { type Config as DOMPurifyConfig } from 'dompurify'
+
+/**
+ * Options de sanitization du corps HTML d'un mail.
+ */
+export type SanitizeMailHtmlOptions = {
+ /**
+ * Si true, les images distantes (http/https) sont affichées directement.
+ * Par défaut false — les images distantes sont remplacées par un placeholder
+ * cliquable pour éviter le tracking par pixel.
+ */
+ allowImages?: boolean
+}
+
+/**
+ * Configuration DOMPurify bloquante pour les corps de mail.
+ * - Bloque les balises dangereuses : script, iframe, object, embed, style, link, meta, form, input
+ * - Bloque les attributs événements (on*) et les URI javascript:
+ * - Autorise les URI data: uniquement pour les images (PNG/JPEG/GIF/WEBP) — images inline CID
+ */
+const DOMPURIFY_CONFIG: DOMPurifyConfig = {
+ FORBID_TAGS: [
+ 'script',
+ 'iframe',
+ 'object',
+ 'embed',
+ 'style',
+ 'link',
+ 'meta',
+ 'form',
+ 'input',
+ 'button',
+ 'textarea',
+ 'select',
+ 'base',
+ 'applet',
+ ],
+ FORBID_ATTR: [
+ 'onerror',
+ 'onload',
+ 'onclick',
+ 'onmouseover',
+ 'onmouseout',
+ 'onmouseenter',
+ 'onmouseleave',
+ 'onfocus',
+ 'onblur',
+ 'onchange',
+ 'onsubmit',
+ 'onreset',
+ 'onkeydown',
+ 'onkeyup',
+ 'onkeypress',
+ 'ondblclick',
+ 'oncontextmenu',
+ 'onwheel',
+ 'ondrag',
+ 'ondrop',
+ 'oncopy',
+ 'oncut',
+ 'onpaste',
+ 'action',
+ 'formaction',
+ 'xlink:href',
+ ],
+ ALLOWED_URI_REGEXP: /^(?:https?|mailto|tel|cid|data:image\/(?:png|jpeg|gif|webp)(?:;base64,)?)(?::|$)/i,
+ FORCE_BODY: true,
+ WHOLE_DOCUMENT: false,
+}
+
+/**
+ * Remplace les balises
avec src http(s):// par un bouton placeholder.
+ * Le src original est stocké en data-mail-image-src pour permettre l'affichage
+ * à la demande de l'utilisateur (Phase 5 — MailMessageViewer).
+ */
+function replaceRemoteImages(html: string): string {
+ // Utiliser un DOMParser côté client uniquement (SSR-safe : le guard process.client
+ // est géré par l'appelant dans un composant Vue — ce helper ne tourne que client-side)
+ const parser = new DOMParser()
+ const doc = parser.parseFromString(html, 'text/html')
+ const images = doc.querySelectorAll('img')
+
+ images.forEach((img) => {
+ const src = img.getAttribute('src') ?? ''
+ const isRemote = /^https?:\/\//i.test(src)
+ if (!isRemote) return
+
+ // Remplacer par un span cliquable (pas de