Side-by-side technical SEO comparison of fmt-web-staging.web.app vs fastmoldtesting.com — confirming the new SSR build won't introduce SEO regressions at cutover.
April 27, 2026 · Prepared by MH-1
Across 15 dimensions tested — title tags, meta descriptions, canonicals, schema JSON-LD, H1 structure, OG/Twitter tags, redirect behavior, 404 handling, security headers, sitemap, robots, and full status-check on all 229 sitemap URLs — staging matches or improves on production. One real regression found: a single city-prefixed nested-route URL returns 404 on staging (details below). Two additional staging-mode items must flip at cutover (robots.txt + page-level robots meta).
The Next.js SSR build is technically clean. Title tags, meta descriptions, canonicals, and schema JSON-LD render identically across homepage, city pages, blog posts, and the blog listing. Internal link counts match. Schema counts match exactly (e.g., 157 BlogPosting entries on /blog, 28 Place objects on /new-york, 8 FAQ Questions on homepage). 404 handling works correctly. Trailing-slash redirects work. Full sitemap spot-check: 228 of 229 URLs return identical status codes on both environments. One nested-route regression to fix before cutover (/sacramento/2026-expert-guide-top-mold-testing-companies-sacramento). The Next.js build also improved staging on two dimensions: HSTS now includes includeSubDomains; preload, and the "Loading locations..." placeholder that was on production is gone. Cleared to ship after the one routing fix.
14 parity dimensions across 6 page templates, validated via direct HTTP requests against both environments at the same point in time (April 27, 2026, 14:30 UTC).
| Dimension | Method | Templates tested |
|---|---|---|
| HTTP response code | curl -I | Homepage, /new-york, /sacramento, /blog, /what-is-a-mold-inspection, 404 routes, malformed URLs |
| Title tag | Regex extraction from raw HTML | All 6 templates |
| Meta description | Regex extraction | All 6 templates |
| Canonical URL | Regex on rel="canonical" | All 6 templates |
| Robots meta tag | Regex on name="robots" | All 6 templates |
| H1 count + content | Regex count of <h1> | All 6 templates |
| Schema JSON-LD types + counts | Regex on "@type" in raw HTML | All 6 templates |
| OG + Twitter tag count | Regex on meta property/name | Homepage |
| Internal link count | Unique href="/path" extraction | Homepage |
| Trailing-slash redirect behavior | curl -L with hop count | /sacramento/ → /sacramento |
| 404 handling | curl against unknown + malformed routes | /unknown-test-route-xyz, /los-angeles) |
| Security headers | curl -I response header inspection | Homepage |
| Sitemap parity | Diff of <loc> URL sets | /sitemap.xml |
| JS rendering / SSR diagnostic | Visible-text count in raw HTML response | /new-york |
| Full sitemap URL status check | Sequential GET with retry/backoff against every URL in both sitemaps | All 229 unique URLs |
All 229 unique URLs across both sitemaps tested with sequential GETs and rate-limit-aware retries on both environments. This is the comprehensive version of the parity check — not just representative templates.
Initial parallel HEAD-request pass flagged 88 staging URLs as failing. On sequential re-test with backoff, 87 of those returned 200 — they were Firebase Hosting throttling, not real regressions. Final results below filter out the rate-limit noise.
| URL | Production | Staging | Verdict |
|---|---|---|---|
/sacramento/2026-expert-guide-top-mold-testing-companies-sacramento | 200 (104 KB content) | 404 | REAL REGRESSION |
This URL is in both sitemaps. Tested 7 candidate variant URLs (flat /2026-expert-guide-..., /blog/..., etc.) — none resolve on either environment, confirming the original is the canonical path. The Next.js routing on staging doesn't handle the /[city]/[blog-slug] nested-route pattern. Fix path: restore the dynamic route, or 301-redirect to a flat-URL replacement and remove from staging sitemap.
| URL | Sitemap presence | Status (prod / staging) | Comment |
|---|---|---|---|
/criticalcontrol | Production only | 200 / 200 | Page exists on both — staging dropped from sitemap. Add back if still relevant. |
/law-pro | Production only | 200 / 200 | Page exists on both — staging dropped from sitemap. Add back if still relevant. |
/advertise | Staging only | 200 / 200 | Page exists on both — staging added it. Add to prod sitemap pre-cutover or accept staging version. |
/partner-program | Staging only | 200 / 200 | Page exists on both — staging added it. Add to prod sitemap pre-cutover or accept staging version. |
/terms | Staging only | 200 / 200 | Page exists on both — staging added it. Add to prod sitemap pre-cutover or accept staging version. |
All 5 sitemap-delta URLs return 200 on both environments. The differences are sitemap inclusion, not page existence. Net: staging sitemap is +1 URL (227 vs 226). No action required unless a specific page should or shouldn't be in the sitemap going forward.
All 14 checks. Where staging differs, the difference is intentional (staging-mode behavior) or an improvement.
| Check | Production | Staging | Match |
|---|---|---|---|
| Homepage HTTP | 200 | 200 | PASS |
| Homepage title tag | "Mold Inspection & Testing Near Me…" | identical | PASS |
| Homepage meta description | "The nation's leader in unbiased mold testing…" | identical | PASS |
| Homepage canonical | https://fastmoldtesting.com | https://fastmoldtesting.com (no leakage to staging URL) | PASS |
| Homepage H1 count | 1 | 1 | PASS |
| Homepage schema types | 11 types: Org, ImageObject, ContactPoint, SearchAction, EntryPoint, AggregateRating, BreadcrumbList, ListItem, FAQPage, Question(8), Answer(8) | identical 11 types, identical counts | PASS |
| Homepage OG/Twitter tags | 8 OG + 6 Twitter | 8 OG + 6 Twitter | PASS |
| Homepage internal hrefs | 71 unique | 71 unique | PASS |
| City page (/new-york) schema | 18 types: ImageObject, PostalAddress, OpeningHoursSpecification(5), GeoCoordinates, AggregateRating, Review(5), Person(5), Rating(5), AdministrativeArea(16), Place(28), City(12), OfferCatalog, Service(4), FAQPage, Question(16), Answer(16), BreadcrumbList, ListItem(2) | identical 18 types, identical counts | PASS |
| City page (/sacramento) schema | 17 types, 13 FAQ Questions, 5 Reviews | identical | PASS |
| Blog post schema | BlogPosting, Person, Org, FAQPage(4), BreadcrumbList | identical | PASS |
| Blog listing schema | Blog + 157 BlogPosting + 157 Person + BreadcrumbList | identical 157/157 | PASS |
| Trailing-slash redirect | /sacramento/ → 301 → /sacramento (1 hop) | identical (1 hop) | PASS |
| 404 for unknown route | 404 | 404 | PASS |
| 404 for malformed URL (/los-angeles)) | 404 | 404 | PASS |
| HSTS header | max-age=31536000 | max-age=31556926; includeSubDomains; preload | IMPROVED |
| X-Content-Type-Options | nosniff | nosniff | PASS |
| X-Frame-Options | SAMEORIGIN | SAMEORIGIN | PASS |
| Referrer-Policy | strict-origin-when-cross-origin | strict-origin-when-cross-origin | PASS |
| Permissions-Policy | camera=(), microphone=(), … | identical | PASS |
| CSP-Report-Only | Production CSP (with Square, Bing Clarity, etc.) | Leaner — verify all production third-party services are still loaded; if so, mirror full prod CSP at cutover | REVIEW |
| Sitemap URL count | 226 URLs | 227 URLs (+1 net; 2 removed: /criticalcontrol, /law-pro; 3 added: /advertise, /partner-program, /terms) | PASS |
| JS rendering — visible text in raw HTML | 17,118 chars (NY page) | 22,793 chars (+33%) | IMPROVED |
| "Loading locations…" placeholder | 1 occurrence (legacy from prior client-render block) | 0 — removed | IMPROVED |
| Robots.txt content | Allow: / + selective Disallow rules | Disallow: / (full block — correct for staging) | FLIP @ CUTOVER |
| Page-level robots meta | index,follow | noindex, nofollow, nocache (correct for staging) | FLIP @ CUTOVER |
One real routing regression plus two correct staging behaviors that need to flip at cutover.
/sacramento/2026-expert-guide-top-mold-testing-companies-sacramento
This URL returns 200 on production (104KB content) but 404 on staging. It's listed in BOTH sitemaps, so after cutover Googlebot will hit a self-declared sitemap URL and get a 404 — bad signal. Tested 7 candidate URL variants; none resolve on either environment, so this is the canonical pattern. The Next.js routing in staging doesn't handle the /[city]/[blog-slug] nested route. Either (A) restore the dynamic route in app/[city]/[slug]/page.tsx or wherever city-scoped blog routes live, or (B) if intentionally retired, set up a 301 from the old URL to its new flat-URL location AND remove from staging sitemap. Out of all 229 sitemap URLs, this is the only real regression.
User-Agent: * / Disallow: / — full crawl block. The production version has selective disallows (/dashboard, /users, /profile, /blog/create, /blog/edit, /blog/manage, /report/view) and an Allow: /. Confirm the production robots.txt ships unchanged at cutover.
<meta name="robots" content="noindex, nofollow, nocache">. This is correct for staging (prevents accidental indexation), but if it ships to production it will deindex the entire site within days. Verify the meta tag is conditional on environment (e.g., process.env.NEXT_PUBLIC_ENV !== 'production') and that production builds emit either index,follow or no robots meta at all.
(1) Pre-cutover: confirm NEXT_PUBLIC_ENV or equivalent is set correctly in production build pipeline. (2) Deploy build to production. (3) Within first 5 minutes post-cutover: curl -I https://fastmoldtesting.com — verify Disallow: / is gone from /robots.txt, and noindex is gone from page-level robots meta. (4) Within first hour: re-fetch homepage + 5 city pages via GSC URL Inspection to confirm Google sees the production version. (5) Within 24 hours: monitor GSC Coverage report for any deindexation signal.
includeSubDomains; preload
Production: max-age=31536000. Staging: max-age=31556926; includeSubDomains; preload. The new directives qualify the domain for the HSTS preload list (hstspreload.org) — once submitted, browsers will refuse non-HTTPS connections to fastmoldtesting.com from a hardcoded list, eliminating the first-visit downgrade attack window. Worth submitting after cutover.
"Loading locations…" — a leftover client-rendered placeholder from the pre-Next.js build. Staging removed it cleanly. The 71 internal hrefs now render server-side with no hydration delay.
These are not parity regressions — they exist on both environments. Flagging them here so the cutover is the right moment to fix them, since both codebases need to update at once.
/mold-inspection-cost) still 404 on both
From the prior audit: this is a high-value content gap targeting the "mold testing cost" AI Overview cluster. Cost guide page deliverable was prepared in /home/exedev/mh2-fast-mold/deliveries/fmt-remaining-fixes/ but hasn't been published. Cutover doesn't change urgency — but it's a clean window.
Scope was set to confirm SEO parity. Items below are out of scope for a parity audit — flag separately if you want them tested.
Technical SEO parity confirmed on 228 of 229 sitemap URLs and across all 15 dimensions tested. The Next.js SSR build is an upgrade, not a sideways move — HSTS strengthened, content rendered earlier, legacy "Loading locations" placeholder removed. One real routing regression on a city-prefixed nested-route URL needs a fix before cutover. Two staging-mode behaviors (full robots.txt block + page-level noindex) MUST flip at cutover, both expected and presumably already environment-gated.
Pre-cutover checklist: (1) Restore the /[city]/[slug] Next.js route OR 301 the one regression URL to its replacement and remove from sitemap. (2) Verify NEXT_PUBLIC_ENV=production is wired and that robots meta + robots.txt are environment-gated.
Cutover sequence: (1) Deploy → (2) immediate post-deploy curl -I /robots.txt + view-source on homepage to confirm robots flipped → (3) curl /sacramento/2026-expert-guide-top-mold-testing-companies-sacramento to confirm the regression fix landed → (4) GSC URL Inspection on homepage + top 5 city pages + the previously-regressed URL to refresh Google's cache → (5) 24-hour Coverage monitoring.
Optional bundle for cutover: publish the cost guide page from the prior fix package while you're shipping. Pre-staged in /home/exedev/mh2-fast-mold/deliveries/fmt-remaining-fixes/.