diff --git a/netlify.toml b/netlify.toml
index 99a58381..ce4feb65 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -9,11 +9,11 @@ package = "@netlify/plugin-nextjs"
for = "/*"
[headers.values]
- # Applies to every host this deploy serves. The host-conditional-noindex
- # edge function strips this header (and the noindex tag) for
- # support.sparkpost.com requests during the CloudFront migration gap, so
- # only archive.sparkpost.com (and deploy previews) actually receive it.
- # See netlify/edge-functions/host-conditional-noindex.ts.
+ # Applies to every host this deploy serves (docs.sparkpost.com, the
+ # archive.sparkpost.com host before it 301s away, and deploy previews).
+ # The docs site is intentionally kept out of search so it doesn't compete
+ # with bird.com — belt-and-braces with the noindex (seo.tsx) and
+ # robots.txt Disallow: / (next-sitemap.js).
X-Robots-Tag = "noindex, nofollow"
Strict-Transport-Security = '''
@@ -54,7 +54,7 @@ package = "@netlify/plugin-nextjs"
'self'
data:
fonts.gstatic.com
- archive.sparkpost.com;
+ docs.sparkpost.com;
connect-src
*
data:
@@ -74,6 +74,17 @@ package = "@netlify/plugin-nextjs"
'none';
'''
+# Permanently redirect the old archived host to its new home. This deploy
+# serves both hostnames; matching on the full archive.sparkpost.com URL sends
+# every archive request (path + query preserved via :splat) to the equivalent
+# docs.sparkpost.com path. Listed first and forced so it wins over the
+# path-based rules below, which then apply once the request is on docs.
+[[redirects]]
+ from = "https://archive.sparkpost.com/*"
+ to = "https://docs.sparkpost.com/:splat"
+ status = 301
+ force = true
+
# A redirect rule with many of the supported properties
[[redirects]]
from = "/"
diff --git a/netlify/edge-functions/host-conditional-noindex.ts b/netlify/edge-functions/host-conditional-noindex.ts
deleted file mode 100644
index b1e1a3db..00000000
--- a/netlify/edge-functions/host-conditional-noindex.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-// Temporary edge function for the support.sparkpost.com → archive.sparkpost.com
-// migration. Until the CloudFront redirect distribution is live (then DNS for
-// support.sparkpost.com flips off Netlify), this single Netlify deploy serves
-// both hostnames. The desired SEO behavior is asymmetric:
-//
-// archive.sparkpost.com: served as built — noindex , X-Robots-Tag, and
-// Disallow: / in robots.txt all intact, so search engines drop the archive.
-// support.sparkpost.com: keep search engines indexing as before, so existing
-// link equity is preserved until the CloudFront layer can 301 it to bird.com.
-//
-// Without this function, the noindex signals (set in seo.tsx, netlify.toml, and
-// next-sitemap.js) would apply to both hostnames equally — which would start
-// deindexing support.sparkpost.com before the 301s exist, losing link equity
-// that would otherwise transfer to bird.com via the CloudFront redirects.
-//
-// This function differentiates by Host header:
-// - support.sparkpost.com: override X-Robots-Tag with "all", strip the noindex
-// from HTML responses, and override /robots.txt with a permissive
-// version.
-// - archive.sparkpost.com or any other host (deploy previews, *.netlify.app):
-// pass through unmodified.
-//
-// REMOVE THIS FUNCTION (and the netlify.toml registration) once the CloudFront
-// cutover is complete — support.sparkpost.com will no longer hit Netlify, so the
-// hostname-conditional logic becomes dead code.
-
-import type { Context } from '@netlify/edge-functions';
-
-const INDEX_HOST = 'support.sparkpost.com';
-
-// Match in either attribute order
-// and with whitespace variations. Only the noindex/nofollow robots meta is
-// targeted — any other meta tags (description, og:*, viewport, etc.) are left
-// alone.
-const NOINDEX_META_PATTERNS: RegExp[] = [
- /]*name=["']robots["'][^>]*content=["'][^"']*noindex[^"']*["'][^>]*\/?>/gi,
- /]*content=["'][^"']*noindex[^"']*["'][^>]*name=["']robots["'][^>]*\/?>/gi,
-];
-
-export default async (request: Request, context: Context): Promise => {
- const host = request.headers.get('host') ?? '';
-
- // Only act on support.sparkpost.com. Every other host — including
- // archive.sparkpost.com, *.netlify.app deploy URLs, and deploy previews —
- // passes through with whatever the build emits (i.e. noindex stays on).
- if (host !== INDEX_HOST) return;
-
- const url = new URL(request.url);
-
- // Override /robots.txt with a permissive version so Google can crawl
- // support.sparkpost.com normally. Without this, the built robots.txt's
- // `Disallow: /` would block Google from re-crawling, which would prevent it
- // from seeing the 301s once CloudFront comes online.
- if (url.pathname === '/robots.txt') {
- return new Response('User-agent: *\n', {
- status: 200,
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
- });
- }
-
- // For everything else, fetch the response normally, then mutate it.
- const response = await context.next();
-
- // Override the X-Robots-Tag set in netlify.toml. `delete()` on this header is
- // silently ignored — Netlify re-applies the netlify.toml [[headers]] block
- // after the edge function returns, but it respects values WE set. So we
- // overwrite with "all" (canonical "ignore any prior noindex; index normally"),
- // which Google treats as equivalent to no header at all.
- response.headers.set('X-Robots-Tag', 'all');
-
- // Only HTML responses can carry the noindex tag. Skip non-HTML
- // (images, JS, CSS, JSON) to avoid pointlessly buffering large bodies.
- const contentType = response.headers.get('content-type') ?? '';
- if (!contentType.includes('text/html')) {
- return response;
- }
-
- let body = await response.text();
- for (const pattern of NOINDEX_META_PATTERNS) {
- body = body.replace(pattern, '');
- }
-
- // Rebuild the response with the mutated body. content-length is dropped so
- // Netlify recomputes it. content-encoding is dropped because response.text()
- // already decoded any gzip/brotli the origin sent — leaving the header on
- // would tell the client to decompress a now-plain-text body and fail.
- const headers = new Headers(response.headers);
- headers.delete('content-length');
- headers.delete('content-encoding');
- return new Response(body, {
- status: response.status,
- statusText: response.statusText,
- headers,
- });
-};
-
-export const config = {
- path: '/*',
-};
diff --git a/next-sitemap.js b/next-sitemap.js
index 73f28ed3..491f464f 100644
--- a/next-sitemap.js
+++ b/next-sitemap.js
@@ -1,5 +1,5 @@
module.exports = {
- siteUrl: process.env.SITE_URL || 'https://archive.sparkpost.com',
+ siteUrl: process.env.SITE_URL || 'https://docs.sparkpost.com',
generateRobotsTxt: true,
sitemapSize: 5000,
robotsTxtOptions: {
diff --git a/tsconfig.json b/tsconfig.json
index 7e38dadd..cce6df6b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -24,5 +24,5 @@
"@context/*": ["context/*"]
},
"include": ["next-env.d.ts", "yaml.d.ts", "**/*.ts", "**/*.tsx"],
- "exclude": ["node_modules", "netlify"]
+ "exclude": ["node_modules"]
}