A private profile that appears in site search or Google results is not private — it is merely hard to navigate to directly. Search engine indexing of private content breaches GDPR Art. 25's privacy-by-default requirement and CWE-200 (information exposure). Users who set their profiles to private expect complete exclusion from discovery, not just a locked profile page. Third-party search indices (Elasticsearch, Algolia) that ingest all records regardless of visibility are a particularly common oversight because indexing pipelines are often built before access control is finalized.
Medium because the exposure is indirect — private content appears in search results rather than being directly accessible — but it defeats the user's explicit privacy intent.
Filter visibility at the search query layer, not post-query. For Elasticsearch, apply a bool.filter clause; for Algolia, use optionalFilters or facetFilters. Emit noindex metadata for private profiles in your page generation.
Next.js generateMetadata example in src/app/u/[username]/page.tsx:
export async function generateMetadata({ params }) {
const user = await db.user.findUnique({ where: { slug: params.username } });
if (user?.profile_visibility !== 'public') {
return { robots: { index: false, follow: false } };
}
return { robots: { index: true, follow: true } };
}
For Elasticsearch, wrap searches with a visibility filter so private records are never returned regardless of requester — do not rely on post-query filtering, which can still expose counts and aggregations.
ID: community-privacy-controls.visibility.search-respects-privacy
Severity: medium
What to look for: Enumerate every relevant item. Examine search implementation (full-text search, Elasticsearch, Algolia, or similar). Check whether the search index includes private profiles or content. Verify that search results are filtered by visibility level. Check robots.txt and meta tags to ensure private content is not indexed by web search engines.
Pass criteria: At least 1 of the following conditions is met. Search results (local site search and web search) exclude private profiles and content. Search indexing respects visibility tiers. Private profiles return 404 to web crawlers or have noindex tags.
Fail criteria: Private profiles appear in search results. Elasticsearch or other indices include all content regardless of visibility. robots.txt or meta tags don't prevent indexing of private content.
Skip (N/A) when: The platform has no search functionality.
Detail on fail: Describe the search issue. Example: "Private profiles appear in full-text search results. No visibility check in search query." or "Elasticsearch index includes all users regardless of visibility setting."
Remediation: Filter search by visibility:
async function searchProfiles(query: string, requesterId: string) {
// With Elasticsearch
const results = await esClient.search({
index: 'users',
body: {
query: {
bool: {
must: [
{ multi_match: { query, fields: ['name', 'bio'] } }
],
filter: [
{
bool: {
should: [
{ term: { visibility: 'public' } },
{ term: { id: requesterId } },
{
nested: {
path: 'followers',
query: { term: { 'followers.id': requesterId } }
}
}
]
}
}
]
}
}
}
});
return results.hits.hits;
}
Prevent web indexing of private content:
// In user profile page (Next.js example)
export const generateMetadata = async ({ params }) => {
const user = await db.user.findUnique({ where: { slug: params.username } });
if (user.visibility === 'private' && !isOwnProfile) {
return { robots: 'noindex, nofollow' };
}
return { robots: 'index, follow' };
}