diff --git a/frontend/libs/common/src/lib/utils/thumbnail-utils.spec.ts b/frontend/libs/common/src/lib/utils/thumbnail-utils.spec.ts
new file mode 100644
--- /dev/null
+++ b/frontend/libs/common/src/lib/utils/thumbnail-utils.spec.ts
@@ -0,0 +1,56 @@
+import { addCorsParam, getThumbnailUrl, getMediaThumbnail } from "./thumbnail-utils";
+
+describe("CORS media URL query parameters", () => {
+  it.each([
+    ["https://cdn.example/audio.mp3?token=a%2Bb", "a+b"],
+    ["/media/video.jpg?frame=1", null],
+  ])("adds a separate cors parameter to %s", (input, token) => {
+    const result = new URL(addCorsParam(input)!, "https://app.example");
+    expect(result.searchParams.get("cors")).toBe("1");
+    if (token !== null) expect(result.searchParams.get("token")).toBe(token);
+    else expect(result.searchParams.get("frame")).toBe("1");
+  });
+
+  it("keeps fragments after the query, where they cannot swallow cors", () => {
+    const result = new URL(addCorsParam("https://cdn.example/image.jpg#preview")!);
+    expect(result.searchParams.get("cors")).toBe("1");
+    expect(result.hash).toBe("#preview");
+  });
+
+  it("preserves signed query bytes and avoids duplicate enabled cors", () => {
+    expect(addCorsParam("https://cdn.example/a?token=a%2Bb&cors=1#preview"))
+      .toBe("https://cdn.example/a?token=a%2Bb&cors=1#preview");
+  });
+
+  it.each(["https://cdn.example/a?cors=0&token=a%2Bb", "https://cdn.example/a?token=a%2Bb&cors"])("enables an existing cors flag without changing other query bytes: %s", (url) => {
+    expect(addCorsParam(url)).toBe(url.replace(/cors(?:=0)?/, "cors=1"));
+  });
+
+  it.each([
+    ["https://cdn.example/image&cors=old.png#preview", "https://cdn.example/image&cors=old.png?cors=1#preview"],
+    ["https://cdn.example/image&cors=old.png?frame=1#preview", "https://cdn.example/image&cors=old.png?frame=1&cors=1#preview"],
+    ["https://cdn.example/image&cors=old.png?cors=0&token=a%2Bb#preview", "https://cdn.example/image&cors=old.png?cors=1&token=a%2Bb#preview"],
+  ])("preserves ampersands in the path of %s", (url, expected) => {
+    const result = addCorsParam(url)!;
+    expect(result).toBe(expected);
+    const parsed = new URL(result);
+    expect(parsed.pathname).toBe(new URL(url).pathname);
+    expect(parsed.searchParams.get("cors")).toBe("1");
+  });
+
+  it("constructs valid CORS URLs through thumbnail callers", () => {
+    const template = "https://cdn.example/{WIDTH}.jpg?frame=1#preview";
+    const thumbnail = getThumbnailUrl(template, { width: 512, addCors: true });
+    const media = getMediaThumbnail({ maybe_video_previews: { animated: template } }, "video", { addCors: true });
+    expect(new URL(thumbnail!).searchParams.get("cors")).toBe("1");
+    expect(new URL(thumbnail!).pathname).toBe("/512.jpg");
+    expect(new URL(media!).searchParams.get("cors")).toBe("1");
+  });
+
+  it("preserves ordinary URLs and empty input behavior", () => {
+    expect(addCorsParam("https://cdn.example/a.jpg")).toBe("https://cdn.example/a.jpg?cors=1");
+    expect(addCorsParam(null)).toBeNull();
+    expect(addCorsParam(undefined)).toBeNull();
+    expect(addCorsParam("")).toBeNull();
+  });
+});
diff --git a/frontend/libs/common/src/lib/utils/thumbnail-utils.ts b/frontend/libs/common/src/lib/utils/thumbnail-utils.ts
--- a/frontend/libs/common/src/lib/utils/thumbnail-utils.ts
+++ b/frontend/libs/common/src/lib/utils/thumbnail-utils.ts
@@ -33,7 +33,22 @@ export function getThumbnailUrl(
 
 export function addCorsParam(url: string | null | undefined): string | null {
   if (!url) return null;
-  return `${url}?cors=1`;
+  const fragmentIndex = url.indexOf("#");
+  const requestUrl = fragmentIndex === -1 ? url : url.slice(0, fragmentIndex);
+  const fragment = fragmentIndex === -1 ? "" : url.slice(fragmentIndex);
+  // Preserve the other query bytes (including signed values) and put the
+  // parameter before the fragment, which is never sent to the CDN.
+  const queryIndex = requestUrl.indexOf("?");
+  if (queryIndex !== -1) {
+    const query = requestUrl.slice(queryIndex + 1);
+    const corsParam = /(^|&)cors(?:=[^&]*)?(?=&|$)/g;
+    if (corsParam.test(query)) {
+      return requestUrl.slice(0, queryIndex + 1) +
+        query.replace(corsParam, "$1cors=1") + fragment;
+    }
+  }
+  const separator = requestUrl.includes("?") ? "&" : "?";
+  return `${requestUrl}${separator}cors=1${fragment}`;
 }
 
 interface MediaThumbnailOptions {
