[object Object]

← back to Govarbitrage

SEO plumbing: sitemap.xml (static pages + every archive date), robots.txt (public routes allowed, app/private disallowed), canonicals from NEWSLETTER_BASE_URL

519132ec7f0b3dd7b43f78cac87046d1709d74c0 · 2026-07-13 01:38:44 -0700 · Steve Abrams

Files touched

Diff

commit 519132ec7f0b3dd7b43f78cac87046d1709d74c0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 13 01:38:44 2026 -0700

    SEO plumbing: sitemap.xml (static pages + every archive date), robots.txt (public routes allowed, app/private disallowed), canonicals from NEWSLETTER_BASE_URL
---
 src/app/robots.ts  | 27 +++++++++++++++++++++++++++
 src/app/sitemap.ts | 27 +++++++++++++++++++++++++++
 2 files changed, 54 insertions(+)

diff --git a/src/app/robots.ts b/src/app/robots.ts
new file mode 100644
index 0000000..8458058
--- /dev/null
+++ b/src/app/robots.ts
@@ -0,0 +1,27 @@
+import type { MetadataRoute } from "next";
+import { publicBaseUrl } from "@/lib/site";
+
+export default function robots(): MetadataRoute.Robots {
+  const base = publicBaseUrl();
+  return {
+    rules: [
+      {
+        userAgent: "*",
+        allow: ["/newsletter", "/pricing", "/deals"],
+        // App/private surfaces: auth-walled anyway, but keep crawlers out of
+        // redirects and per-listing buyer pages entirely.
+        disallow: [
+          "/api/",
+          "/listings",
+          "/reports",
+          "/selling-avenues",
+          "/billing",
+          "/credentials",
+          "/login",
+          "/b/",
+        ],
+      },
+    ],
+    sitemap: `${base}/sitemap.xml`,
+  };
+}
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts
new file mode 100644
index 0000000..d44fe7f
--- /dev/null
+++ b/src/app/sitemap.ts
@@ -0,0 +1,27 @@
+import type { MetadataRoute } from "next";
+import { listDigestDates } from "@/lib/digest-snapshot";
+import { publicBaseUrl } from "@/lib/site";
+
+export const dynamic = "force-dynamic";
+
+export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
+  const base = publicBaseUrl();
+  const dates = await listDigestDates();
+
+  const staticPages: MetadataRoute.Sitemap = [
+    { url: `${base}/`, changeFrequency: "daily", priority: 0.8 },
+    { url: `${base}/newsletter`, changeFrequency: "weekly", priority: 1 },
+    { url: `${base}/pricing`, changeFrequency: "weekly", priority: 0.7 },
+    { url: `${base}/deals`, changeFrequency: "daily", priority: 0.9 },
+  ];
+
+  return [
+    ...staticPages,
+    ...dates.map(({ date, lastModified }) => ({
+      url: `${base}/deals/${date}`,
+      lastModified,
+      changeFrequency: "monthly" as const, // frozen snapshots — they don't change
+      priority: 0.6,
+    })),
+  ];
+}

← 5e22c50 Public SEO deal archive: /deals index + /deals/[date] teaser  ·  back to Govarbitrage  ·  auto-save: 2026-07-13T01:52:00 (8 files) — prisma/schema.pri dba59ea →