diff --git a/surfsense_web/app/(home)/announcements/layout.tsx b/surfsense_web/app/(home)/announcements/layout.tsx index 072db2c3f..e5102b85e 100644 --- a/surfsense_web/app/(home)/announcements/layout.tsx +++ b/surfsense_web/app/(home)/announcements/layout.tsx @@ -5,12 +5,12 @@ export const metadata: Metadata = { title: "Announcements | SurfSense", description: "Latest product updates, feature releases, and news from SurfSense.", alternates: { - canonical: "https://surfsense.com/announcements", + canonical: "https://www.surfsense.com/announcements", }, openGraph: { title: "Announcements | SurfSense", description: "Latest product updates, feature releases, and news from SurfSense.", - url: "https://surfsense.com/announcements", + url: "https://www.surfsense.com/announcements", type: "website", }, twitter: { diff --git a/surfsense_web/app/(home)/blog/[slug]/page.tsx b/surfsense_web/app/(home)/blog/[slug]/page.tsx index 79787b774..1f4d62968 100644 --- a/surfsense_web/app/(home)/blog/[slug]/page.tsx +++ b/surfsense_web/app/(home)/blog/[slug]/page.tsx @@ -4,7 +4,8 @@ import Image from "next/image"; import { notFound } from "next/navigation"; import { blog } from "@/.source/server"; import { BreadcrumbNav } from "@/components/seo/breadcrumb-nav"; -import { ArticleJsonLd } from "@/components/seo/json-ld"; +import { ArticleJsonLd, FAQJsonLd } from "@/components/seo/json-ld"; +import { extractFaqFromBlogPost } from "@/lib/blog-faq"; import { formatDate } from "@/lib/utils"; import { getMDXComponents } from "@/mdx-components"; @@ -21,6 +22,8 @@ interface BlogData { author?: string; authorAvatar?: string; tags?: string[]; + // Populated by Fumadocs when `lastModifiedTime: "git"` is set in source.config.ts. + lastModified?: Date; body: React.ComponentType<{ components?: Record; }>; @@ -50,7 +53,7 @@ export async function generateMetadata(props: { title: `${page.data.title} | SurfSense Blog`, description: page.data.description, alternates: { - canonical: `https://surfsense.com/blog/${slug}`, + canonical: `https://www.surfsense.com/blog/${slug}`, }, openGraph: { title: page.data.title, @@ -78,17 +81,23 @@ export default async function BlogPostPage(props: { params: Promise<{ slug: stri const MDX = page.data.body; const date = new Date(page.data.date); + const dateModified = page.data.lastModified + ? new Date(page.data.lastModified).toISOString() + : undefined; + const faqEntries = await extractFaqFromBlogPost(slug); return (
+ {faqEntries.length > 0 && }
const title = buildSeoTitle(model); const description = buildSeoDescription(model); - const canonicalUrl = `https://surfsense.com/free/${model.seo_slug}`; + const canonicalUrl = `https://www.surfsense.com/free/${model.seo_slug}`; const modelNameLower = model.name.toLowerCase(); return { @@ -161,7 +161,7 @@ export default async function FreeModelPage({ params }: PageProps) { "@type": "WebApplication", name: `${model.name} Free Chat Without Login - SurfSense`, description, - url: `https://surfsense.com/free/${model.seo_slug}`, + url: `https://www.surfsense.com/free/${model.seo_slug}`, applicationCategory: "ChatApplication", operatingSystem: "Web", offers: { @@ -173,12 +173,12 @@ export default async function FreeModelPage({ params }: PageProps) { provider: { "@type": "Organization", name: "SurfSense", - url: "https://surfsense.com", + url: "https://www.surfsense.com", }, isPartOf: { "@type": "WebSite", name: "SurfSense", - url: "https://surfsense.com", + url: "https://www.surfsense.com", }, }} /> diff --git a/surfsense_web/app/(home)/free/page.tsx b/surfsense_web/app/(home)/free/page.tsx index 3ddd5195f..cd75e7908 100644 --- a/surfsense_web/app/(home)/free/page.tsx +++ b/surfsense_web/app/(home)/free/page.tsx @@ -64,13 +64,13 @@ export const metadata: Metadata = { "notebooklm alternative", ], alternates: { - canonical: "https://surfsense.com/free", + canonical: "https://www.surfsense.com/free", }, openGraph: { title: "Free AI Chat, No Login Required | SurfSense", description: "Use ChatGPT free online without login. Chat with GPT-4, Claude AI, Gemini and 100+ AI models. Open source NotebookLM alternative.", - url: "https://surfsense.com/free", + url: "https://www.surfsense.com/free", siteName: "SurfSense", type: "website", images: [ @@ -164,8 +164,8 @@ export default async function FreeHubPage() { name: "ChatGPT Free Online Without Login - SurfSense", description: "Use ChatGPT, Claude AI, Gemini and more AI models free online without login or sign-up. Open source NotebookLM alternative with no login required.", - url: "https://surfsense.com/free", - isPartOf: { "@type": "WebSite", name: "SurfSense", url: "https://surfsense.com" }, + url: "https://www.surfsense.com/free", + isPartOf: { "@type": "WebSite", name: "SurfSense", url: "https://www.surfsense.com" }, mainEntity: { "@type": "ItemList", numberOfItems: seoModels.length, @@ -173,7 +173,7 @@ export default async function FreeHubPage() { "@type": "ListItem", position: i + 1, name: m.name, - url: `https://surfsense.com/free/${m.seo_slug}`, + url: `https://www.surfsense.com/free/${m.seo_slug}`, })), }, }} diff --git a/surfsense_web/app/(home)/pricing/page.tsx b/surfsense_web/app/(home)/pricing/page.tsx index 2a413b9a9..b343a3853 100644 --- a/surfsense_web/app/(home)/pricing/page.tsx +++ b/surfsense_web/app/(home)/pricing/page.tsx @@ -7,7 +7,7 @@ export const metadata: Metadata = { description: "Explore SurfSense plans and pricing. Start free with 500 pages & $5 in premium credits. Use ChatGPT, Claude AI, and premium AI models. Pay as you go at provider cost.", alternates: { - canonical: "https://surfsense.com/pricing", + canonical: "https://www.surfsense.com/pricing", }, }; diff --git a/surfsense_web/app/(home)/privacy/page.tsx b/surfsense_web/app/(home)/privacy/page.tsx index cbafd6621..1bc939a1f 100644 --- a/surfsense_web/app/(home)/privacy/page.tsx +++ b/surfsense_web/app/(home)/privacy/page.tsx @@ -4,7 +4,7 @@ export const metadata: Metadata = { title: "Privacy Policy | SurfSense", description: "Privacy Policy for SurfSense application", alternates: { - canonical: "https://surfsense.com/privacy", + canonical: "https://www.surfsense.com/privacy", }, }; diff --git a/surfsense_web/app/(home)/terms/page.tsx b/surfsense_web/app/(home)/terms/page.tsx index 012e7c76a..5ec14aeaa 100644 --- a/surfsense_web/app/(home)/terms/page.tsx +++ b/surfsense_web/app/(home)/terms/page.tsx @@ -4,7 +4,7 @@ export const metadata: Metadata = { title: "Terms of Service | SurfSense", description: "Terms of Service for SurfSense application", alternates: { - canonical: "https://surfsense.com/terms", + canonical: "https://www.surfsense.com/terms", }, }; diff --git a/surfsense_web/app/docs/[[...slug]]/page.tsx b/surfsense_web/app/docs/[[...slug]]/page.tsx index 5d1f4ff53..b7a3d7b66 100644 --- a/surfsense_web/app/docs/[[...slug]]/page.tsx +++ b/surfsense_web/app/docs/[[...slug]]/page.tsx @@ -50,7 +50,7 @@ export async function generateMetadata(props: { params: Promise<{ slug?: string[ title: `${page.data.title} | SurfSense Docs`, description: page.data.description, alternates: { - canonical: `https://surfsense.com/docs${slugPath ? `/${slugPath}` : ""}`, + canonical: `https://www.surfsense.com/docs${slugPath ? `/${slugPath}` : ""}`, }, openGraph: { title: `${page.data.title} | SurfSense Docs`, diff --git a/surfsense_web/app/layout.tsx b/surfsense_web/app/layout.tsx index 144968a2b..3c9943644 100644 --- a/surfsense_web/app/layout.tsx +++ b/surfsense_web/app/layout.tsx @@ -41,9 +41,9 @@ export const viewport: Viewport = { }; export const metadata: Metadata = { - metadataBase: new URL("https://surfsense.com"), + metadataBase: new URL("https://www.surfsense.com"), alternates: { - canonical: "https://surfsense.com", + canonical: "https://www.surfsense.com", }, title: "SurfSense – Open Source, Privacy-Focused NotebookLM Alternative for Teams", description: @@ -90,7 +90,7 @@ export const metadata: Metadata = { title: "SurfSense – Open Source, Privacy-Focused NotebookLM Alternative for Teams", description: "Open source NotebookLM alternative for teams with no data limits. Use ChatGPT, Claude, and any AI model for free.", - url: "https://surfsense.com", + url: "https://www.surfsense.com", siteName: "SurfSense", type: "website", images: [ diff --git a/surfsense_web/app/robots.ts b/surfsense_web/app/robots.ts index ef4f57668..085c3d9a3 100644 --- a/surfsense_web/app/robots.ts +++ b/surfsense_web/app/robots.ts @@ -17,6 +17,6 @@ export default function robots(): MetadataRoute.Robots { ], }, ], - sitemap: "https://surfsense.com/sitemap.xml", + sitemap: "https://www.surfsense.com/sitemap.xml", }; } diff --git a/surfsense_web/blog/content/no-login-ai-privacy-reality-check.mdx b/surfsense_web/blog/content/no-login-ai-privacy-reality-check.mdx index 4eb87c9f3..79e199a70 100644 --- a/surfsense_web/blog/content/no-login-ai-privacy-reality-check.mdx +++ b/surfsense_web/blog/content/no-login-ai-privacy-reality-check.mdx @@ -40,7 +40,7 @@ The promise of the search query "AI without login" is simple: someone wants to c Whatever the reason, the answer in 2026 is mostly: **yes, you can do this**, but the options are scattered across product pages, browser features, and a long tail of wrapper sites that look identical. This guide is the cleanest map we could draw, organised so a casual user gets value in the first three minutes and a developer or privacy-conscious reader can keep going deeper. -We're going to cite primary sources for everything that touches privacy or product behavior, so you can verify (and so the article ages well as the products change). The two cluster posts that go deeper into [Claude specifically](/blog/use-claude-without-login-2026) and into [a tested comparison of 12 free AI chats](/blog/tested-no-login-ai-chats-2026) will sit alongside this one when they're ready. +We're going to cite primary sources for everything that touches privacy or product behavior, so you can verify (and so the article ages well as the products change). ## Where to use each major AI model without an account @@ -70,9 +70,9 @@ Anthropic does require an account on `claude.ai` itself, but the picture is much **Path 3: Brave Leo (no account, browser-side).** Install the [Brave browser](https://brave.com), open the sidebar, click the Leo icon, pick Claude Haiku from the model dropdown. No signup. Brave doesn't collect identifiers tied to you ([per their docs](https://brave.com/leo/)). The trade-off is that you have to use Brave as your browser, and you're limited to Haiku on the free tier (Sonnet and Opus require Brave Leo Premium at $14.99/month). -**Path 4: Multi-model aggregator pages.** These wrap the Anthropic API and serve Claude responses without an account on Anthropic. The pick we'd recommend (with the obvious disclosure that we made it) is **[SurfSense /free](/free)**: it lists Claude alongside ChatGPT, Gemini, DeepSeek, Mistral, and Llama in one chat UI, the source code is open on [GitHub](https://github.com/MODSetter/SurfSense) so the privacy and quota behavior is verifiable, and the 500K free token quota is shared across any model you pick (so you can spend the budget on Claude if that's what you came for). The closed-source alternatives ([HIX.AI](https://hix.ai/claude), [EaseMate](https://www.easemate.ai/ai-chat/ask-claude), [Eye2.ai](https://eye2.ai), [NoteGPT](https://notegpt.io/ai-models/claude-sonnet-4-5)) work too, but quality and message limits vary widely; we [tested 12 of these](/blog/tested-no-login-ai-chats-2026) in a separate post. +**Path 4: Multi-model aggregator pages.** These wrap the Anthropic API and serve Claude responses without an account on Anthropic. The pick we'd recommend (with the obvious disclosure that we made it) is **[SurfSense /free](/free)**: it lists Claude alongside ChatGPT, Gemini, DeepSeek, Mistral, and Llama in one chat UI, the source code is open on [GitHub](https://github.com/MODSetter/SurfSense) so the privacy and quota behavior is verifiable, and the 500K free token quota is shared across any model you pick (so you can spend the budget on Claude if that's what you came for). The closed-source alternatives ([HIX.AI](https://hix.ai/claude), [EaseMate](https://www.easemate.ai/ai-chat/ask-claude), [Eye2.ai](https://eye2.ai), [NoteGPT](https://notegpt.io/ai-models/claude-sonnet-4-5)) work too, but quality and message limits vary widely. -For the developer-specific paths (Claude Code with Bedrock or Vertex AI authentication, the Claude for Open Source program, the `/passes` Guest Pass system), see our [Claude-specific deep dive](/blog/use-claude-without-login-2026). +For the developer-specific paths (Claude Code with Bedrock or Vertex AI authentication, the Claude for Open Source program), see Anthropic's [Claude Code authentication docs](https://code.claude.com/docs/en/authentication). ### Gemini, the awkward one @@ -242,7 +242,7 @@ Yes. Open `chatgpt.com` in a private tab and you'll get guest mode automatically ### Can I use Claude without an account? -Not on `claude.ai` itself, which still requires signup. The closest no-account paths are [Duck.ai](https://duck.ai) (Claude Haiku 4.5, free, anonymised), [Brave Leo](https://brave.com/leo/) (Claude Haiku in the Brave browser sidebar), and aggregator pages like our open-source [SurfSense /free](/free), which lists Claude among the models you can pick with no Anthropic account and a 500K free token budget shared across the whole page. For more, see our [Claude-specific guide](/blog/use-claude-without-login-2026). +Not on `claude.ai` itself, which still requires signup. The closest no-account paths are [Duck.ai](https://duck.ai) (Claude Haiku 4.5, free, anonymised), [Brave Leo](https://brave.com/leo/) (Claude Haiku in the Brave browser sidebar), and aggregator pages like our open-source [SurfSense /free](/free), which lists Claude among the models you can pick with no Anthropic account and a 500K free token budget shared across the whole page. ### Can I use Gemini without a Google account? diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx index acece2d5c..b17b0729e 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx @@ -53,7 +53,7 @@ const THEMES = [ ]; const LEARN_MORE_LINKS = [ - { key: "documentation" as const, href: "https://surfsense.com/docs" }, + { key: "documentation" as const, href: "https://www.surfsense.com/docs" }, { key: "github" as const, href: "https://github.com/MODSetter/SurfSense" }, ]; diff --git a/surfsense_web/components/seo/breadcrumb-nav.tsx b/surfsense_web/components/seo/breadcrumb-nav.tsx index cfd8a3d75..824a695a5 100644 --- a/surfsense_web/components/seo/breadcrumb-nav.tsx +++ b/surfsense_web/components/seo/breadcrumb-nav.tsx @@ -15,7 +15,7 @@ interface BreadcrumbNavProps { export function BreadcrumbNav({ items, className }: BreadcrumbNavProps) { const jsonLdItems = items.map((item) => ({ name: item.name, - url: `https://surfsense.com${item.href}`, + url: `https://www.surfsense.com${item.href}`, })); return ( diff --git a/surfsense_web/components/seo/json-ld.tsx b/surfsense_web/components/seo/json-ld.tsx index dee81f153..03f3293c3 100644 --- a/surfsense_web/components/seo/json-ld.tsx +++ b/surfsense_web/components/seo/json-ld.tsx @@ -16,8 +16,8 @@ export function OrganizationJsonLd() { "@context": "https://schema.org", "@type": "Organization", name: "SurfSense", - url: "https://surfsense.com", - logo: "https://surfsense.com/logo.png", + url: "https://www.surfsense.com", + logo: "https://www.surfsense.com/logo.png", description: "Open source NotebookLM alternative for teams with no data limits. Use ChatGPT, Claude AI, and any AI model for free.", sameAs: ["https://github.com/MODSetter/SurfSense", "https://discord.gg/Cg2M4GUJ"], @@ -38,14 +38,14 @@ export function WebSiteJsonLd() { "@context": "https://schema.org", "@type": "WebSite", name: "SurfSense", - url: "https://surfsense.com", + url: "https://www.surfsense.com", description: "Open source NotebookLM alternative for teams with no data limits. Free ChatGPT, Claude AI, and any AI model.", potentialAction: { "@type": "SearchAction", target: { "@type": "EntryPoint", - urlTemplate: "https://surfsense.com/docs?search={search_term_string}", + urlTemplate: "https://www.surfsense.com/docs?search={search_term_string}", }, "query-input": "required name=search_term_string", }, @@ -71,7 +71,7 @@ export function SoftwareApplicationJsonLd() { }, description: "Open source NotebookLM alternative with free access to ChatGPT, Claude AI, and any model. Connect Slack, Google Drive, Notion, Confluence, GitHub, and dozens more data sources.", - url: "https://surfsense.com", + url: "https://www.surfsense.com", downloadUrl: "https://github.com/MODSetter/SurfSense/releases", featureList: [ "Free access to ChatGPT, Claude AI, and any AI model", @@ -95,6 +95,7 @@ export function ArticleJsonLd({ description, url, datePublished, + dateModified, author, image, }: { @@ -102,6 +103,7 @@ export function ArticleJsonLd({ description: string; url: string; datePublished: string; + dateModified?: string; author: string; image?: string; }) { @@ -114,6 +116,7 @@ export function ArticleJsonLd({ description, url, datePublished, + ...(dateModified ? { dateModified } : {}), author: { "@type": "Organization", name: author, @@ -123,10 +126,10 @@ export function ArticleJsonLd({ name: "SurfSense", logo: { "@type": "ImageObject", - url: "https://surfsense.com/logo.png", + url: "https://www.surfsense.com/logo.png", }, }, - image: image || "https://surfsense.com/og-image.png", + image: image || "https://www.surfsense.com/og-image.png", mainEntityOfPage: { "@type": "WebPage", "@id": url, diff --git a/surfsense_web/lib/blog-faq.ts b/surfsense_web/lib/blog-faq.ts new file mode 100644 index 000000000..d1572612e --- /dev/null +++ b/surfsense_web/lib/blog-faq.ts @@ -0,0 +1,54 @@ +import { promises as fs } from "node:fs"; +import path from "node:path"; + +export interface FaqEntry { + question: string; + answer: string; +} + +/** + * Extracts FAQ items from a blog post MDX file by parsing the `## FAQ` section. + * + * The FAQ section is bounded by `## FAQ` and the next H2 heading. Each H3 inside + * is treated as a question, with the body until the next H3 (or the end of the + * FAQ section) as its answer. Common Markdown decorations (links, bold, inline + * code) are stripped so the JSON-LD output contains plain text suitable for + * Google's FAQ rich-result eligibility checks. + * + * Returns an empty array when the post has no FAQ section, or when the file + * cannot be read (e.g. for posts that do not yet exist on disk). + */ +export async function extractFaqFromBlogPost(slug: string): Promise { + try { + const filepath = path.join(process.cwd(), "blog", "content", `${slug}.mdx`); + const content = await fs.readFile(filepath, "utf-8"); + return extractFaqFromContent(content); + } catch { + return []; + } +} + +export function extractFaqFromContent(content: string): FaqEntry[] { + const faqHeading = content.match(/^##\s+FAQ\s*$/m); + if (!faqHeading || faqHeading.index === undefined) return []; + + const afterFaq = content.slice(faqHeading.index + faqHeading[0].length); + const nextH2 = afterFaq.match(/^##\s+/m); + const faqBody = nextH2 ? afterFaq.slice(0, nextH2.index) : afterFaq; + + const blocks = faqBody.split(/^###\s+/m).slice(1); + + return blocks + .map((block) => { + const newlineIdx = block.indexOf("\n"); + const question = (newlineIdx === -1 ? block : block.slice(0, newlineIdx)).trim(); + const answer = (newlineIdx === -1 ? "" : block.slice(newlineIdx + 1)) + .trim() + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/`([^`]+)`/g, "$1") + .replace(/\s+/g, " "); + return { question, answer }; + }) + .filter((item) => item.question.length > 0 && item.answer.length > 0); +}