# Public Profile SEO & OpenGraph

- **Pattern:** `ab-000759` (`community-social-engagement.profiles-identity.profile-seo-metadata`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000759
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000759)

## Why it matters

Private profile pages that emit OpenGraph tags leak user identity to search crawlers and social preview scrapers, violating GDPR Art. 25 (data protection by design). A user who marks their profile private reasonably expects that `og:title`, `og:image`, and `og:description` do not appear in Google's index or in link previews shared by third parties. Simultaneously, public profiles without OpenGraph metadata miss every social share amplification opportunity — both failure modes live in the same `generateMetadata` function.

## Severity rationale

Medium because the leak is passive — crawlers must visit the page — but the privacy violation is real and the SEO cost of missing metadata is measurable.

## Remediation

In `app/users/[id]/page.tsx`, return `robots: 'noindex'` from `generateMetadata` whenever the profile is private or the viewer is unauthorized. For public profiles, populate the full OpenGraph object.

```ts
// app/users/[id]/page.tsx
export async function generateMetadata({ params }: { params: { id: string } }) {
  const user = await getUserProfile(params.id)

  if (!user || user.is_private) {
    return { robots: { index: false, follow: false } }
  }

  return {
    title: `${user.name} — YourApp`,
    description: user.bio ?? `See ${user.name}'s profile on YourApp`,
    openGraph: {
      title: user.name,
      description: user.bio ?? '',
      images: user.avatar ? [{ url: user.avatar, width: 400, height: 400 }] : [],
      type: 'profile'
    }
  }
}
```

## Detection

- **ID:** `profile-seo-metadata`
- **Severity:** `medium`
- **What to look for:** Enumerate all relevant files and Check the dynamic profile route for metadata generation. Look for `generateMetadata` functions (Next.js App Router) or `getStaticProps`/`getServerSideProps` (Pages Router) that set OpenGraph tags based on the user's profile data. Verify that private profiles do not generate indexable metadata and have `noindex` headers or meta tags when viewing a private profile as an unauthorized user.
- **Pass criteria:** At least 1 implementation must be present. Public profile pages generate OpenGraph metadata including `og:title`, `og:description`, and `og:image` based on the user's profile. Private profiles are either not indexable or return 404/401 to unauthenticated users.
- **Fail criteria:** No metadata generation for profiles, or private profiles generate OpenGraph tags that leak information to crawlers.
- **Skip (N/A) when:** Profiles are not public-facing or SEO is not a concern for this platform.
- **Detail on fail:** `"Profile pages have no generateMetadata function. OpenGraph tags are not set."` or `"Private profile pages generate og:title even when viewer is not authorized"`
- **Remediation:** Generate metadata for public profiles:

  ```ts
  // app/users/[id]/page.tsx
  export async function generateMetadata({ params }) {
    const user = await getUserProfile(params.id)

    if (!user || user.is_private) {
      return { robots: 'noindex' }
    }

    return {
      title: `${user.name} on YourApp`,
      description: user.bio || `Profile of ${user.name}`,
      openGraph: {
        title: user.name,
        description: user.bio,
        images: [{ url: user.avatar, width: 200, height: 200 }],
        type: 'profile'
      }
    }
  }
  ```

---

## External references

- gdpr Art. 25

Taxons: findability, privacy-consent

HTML version: https://auditbuffet.com/patterns/ab-000759
