Skip to content

Custom domain share pages emit cap.so Open Graph metadata, so Slack shows no thumbnail #2122

Description

@dmdfajardo00

Summary

A share page served on a verified custom domain emits Open Graph metadata that points at cap.so. The og:url, og:image, og:video and <link rel="canonical"> values all use NEXT_PUBLIC_WEB_URL. Slack reads that mismatch and drops the card down to a bare title and description. No thumbnail appears.

Steps to reproduce

  1. Set a custom domain on an organization and verify it.
  2. Open a public share link on the custom domain. Example: https://looms.dmdfajardo.pro/s/6g6r5spzmwf3jkn.
  3. Read the response with a crawler user agent.
  4. Post the same link in Slack.

What happens

The custom domain returns the exact same meta tags as cap.so:

<meta property="og:url"   content="https://cap.so/s/6g6r5spzmwf3jkn"/>
<meta property="og:image" content="https://cap.so/api/video/preview?videoId=6g6r5spzmwf3jkn&fallback=og"/>
<meta property="og:image" content="https://cap.so/api/video/og?videoId=6g6r5spzmwf3jkn"/>
<meta property="og:video" content="https://cap.so/api/playlist?videoId=6g6r5spzmwf3jkn&videoType=mp4"/>
<link rel="canonical"     href="https://cap.so/s/6g6r5spzmwf3jkn"/>

Slack renders the cap.so link as a full video card. Slack renders the custom domain link as a title and a description with no image.

The image endpoints work on the custom domain. https://looms.dmdfajardo.pro/api/video/og?videoId=... returns HTTP 200 and image/png. Only the advertised URLs are wrong.

Cause

generateMetadata runs before the custom domain lookup and never receives it.

  • apps/web/app/s/[videoId]/page.tsx:256 passes webUrl: buildEnv.NEXT_PUBLIC_WEB_URL into buildShareVideoMetadata.
  • apps/web/app/s/[videoId]/page.tsx:274, :294 and :308 hardcode the same value in the restricted and password-protected branches.
  • apps/web/app/s/[videoId]/page.tsx:531-558 resolves customDomain and domainVerified, but the page component uses the result. The metadata path does not.
  • apps/web/lib/share-video-metadata.ts builds every absolute URL from the webUrl argument, so a correct argument fixes the whole block.

The request already knows the host. The response sets verified_domain=looms.dmdfajardo.pro as a cookie, and x-nextjs-rewritten-path shows the rewrite.

Expected behavior

When a request arrives on a verified custom domain, the share page emits og:url, og:image, og:video and canonical on that domain.

Suggested fix

  1. Read the verified custom domain inside generateMetadata, from the request host or from the same organization lookup the page uses.
  2. Pass it as webUrl to buildShareVideoMetadata.
  3. Use the same value in the PolicyDenied and VerifyVideoPasswordError branches.
  4. Fall back to buildEnv.NEXT_PUBLIC_WEB_URL when the domain is absent or unverified.

Related

apps/web/slack-app-manifest.json sets "unfurl_domains": ["cap.so", "cap.link"], so the Cap Slack app never unfurls a custom domain. A custom domain link always falls back to the generic Slack crawler. That makes correct Open Graph metadata the only preview path those links have.

Issue #220 covers a different case. It reports thumbnails that point at production during local development.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions