Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions server/sitemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,31 @@ export async function sitemaps() {
console.info('Preparing Sitemap...');
const { manifest } = await import('../build/server/manifest.js');
const threads = collectThreads().map((id) => `/threads/${id}`);

// Internal/auth paths to exclude
const INTERNAL_PATHS = ['/console/login', '/console/register', '/v1/'];

const otherRoutes = manifest._.routes
.filter((r) => r.params.length === 0)
.map((r) => r.id)
.filter(
(id) => !id.startsWith('/threads/') && !id.endsWith('.json') && !id.endsWith('.xml')
);
.filter((id) => {
// Exclude threads (handled separately), JSON/XML endpoints
if (id.startsWith('/threads/') || id.endsWith('.json') || id.endsWith('.xml')) {
return false;
}

// Exclude any docs references that are not the canonical \"cloud\" version
if (id.startsWith('/docs/references/') && !id.startsWith('/docs/references/cloud/')) {
return false;
}

// Exclude internal/auth paths
if (INTERNAL_PATHS.some((path) => id.startsWith(path))) {
return false;
}

return true;
});

mkdirSync(SITEMAP_DIR, { recursive: true });
mkdirSync(THREADS_DIR, { recursive: true });
Expand Down
33 changes: 32 additions & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,43 @@ const initSession: Handle = async ({ event, resolve }) => {
return resolve(event);
};

/**
* SEO optimization: noindex internal/auth pages and staging subdomains
*/
const NOINDEX_PATHS = [
/^\/console\/login\/?$/,
/^\/console\/register\/?$/,
/^\/v1\/storage\//,
/^\/v1\//
];

// Block any staging/preview subdomains (e.g., *.cloud.appwrite.io, stage.*, etc.)
const NOINDEX_HOSTS = [/\.cloud\.appwrite\.io$/i, /^stage\./i, /^fra\./i];

const seoOptimization: Handle = async ({ event, resolve }) => {
const { url } = event;

// Check if this is a path or host that should not be indexed
const shouldNoindex =
NOINDEX_PATHS.some((re) => re.test(url.pathname)) ||
NOINDEX_HOSTS.some((re) => re.test(url.hostname));

const response = await resolve(event);

if (shouldNoindex) {
response.headers.set('x-robots-tag', 'noindex, nofollow');
}

return response;
};

export const handle = sequence(
Sentry.sentryHandle(),
markdownHandler,
redirecter,
wwwRedirecter,
securityheaders,
initSession
initSession,
seoOptimization
);
export const handleError = Sentry.handleErrorWithSentry();
18 changes: 7 additions & 11 deletions src/lib/utils/canonical.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { DEFAULT_HOST } from './metadata';

/**
* Canonical version for API references - should always point to this version
*/
const CANONICAL_VERSION = 'cloud';

/**
* Normalizes the version in a docs reference path to the canonical version
*/
function normalizeDocsVersion(pathname: string): string {
// Match patterns like /docs/references/1.7.x/... or /docs/references/0.15.x/... or /docs/references/1.8.x/...
// Match patterns like /docs/references/1.7.x/... or /docs/references/0.15.x/... or /docs/references/cloud/...
const versionPattern = /^\/docs\/references\/([^/]+)\//;
const match = pathname.match(versionPattern);

if (match) {
const currentVersion = match[1];
// Always point to cloud version for canonical URLs
// 'cloud' maps to 1.8.x server-side, and is the preferred canonical version
if (currentVersion !== CANONICAL_VERSION) {
return pathname.replace(versionPattern, `/docs/references/${CANONICAL_VERSION}/`);
// Use "cloud" as the single canonical docs version so we don't have to
// maintain a list of explicit versions. Anything that isn't "cloud"
// will point canonically to the /cloud/ variant.
if (currentVersion !== 'cloud') {
return pathname.replace(versionPattern, `/docs/references/cloud/`);
}
}

Expand All @@ -28,7 +24,7 @@ function normalizeDocsVersion(pathname: string): string {
/**
* Builds a canonical URL from a page URL, applying smart normalization:
* - Fixes prerender origin issue (uses DEFAULT_HOST during prerendering)
* - For docs references: always points to cloud version
* - For docs references: always points to the chosen canonical version
* - Query strings are already stripped by using pathname (original behavior preserved)
*/
export function getCanonicalUrl(url: URL): string {
Expand Down
18 changes: 18 additions & 0 deletions src/routes/docs/references/[version]/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { page } from '$app/state';
import type { Snippet } from 'svelte';
const { children }: { children: Snippet } = $props();
// Check if the current version is not the canonical "cloud" version
const isOldVersion = $derived(page.params?.version && page.params.version !== 'cloud');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rather isNotCloud so its a bit more clear?

</script>

<svelte:head>
{#if isOldVersion}
<!-- Noindex old documentation versions to avoid duplicates -->
<meta name="robots" content="noindex, follow" />
{/if}
</svelte:head>

{@render children()}
23 changes: 22 additions & 1 deletion src/routes/robots.txt/+server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
import type { RequestHandler } from '@sveltejs/kit';

const follow = `# robotstxt.org/
User-agent: *`;
User-agent: *

# Block tracking parameters to avoid duplicate indexing
Disallow: /*?utm_
Disallow: /*&utm_
Disallow: /*?ref=
Disallow: /*&ref=
Disallow: /*?trk=
Disallow: /*&trk=
Disallow: /*?adobe_mc=
Disallow: /*&adobe_mc=

# Block internal/auth pages
Disallow: /console/login
Disallow: /console/register
Disallow: /v1/

# Block all versioned docs references
Disallow: /docs/references/*/

# Allow canonical cloud docs
Allow: /docs/references/cloud/`;

const nofollow = `# robotstxt.org/
User-agent: *
Expand Down