Static hosting hands you the performance half of technical SEO for free. What it does not handle automatically is the part that causes real problems: preview deployments getting indexed, redirects returning the wrong status code, inconsistent trailing slashes creating duplicate URLs, and canonical tags pointing at the wrong domain.
The good news is that a static site on a CDN starts from a strong position — fast responses, HTTPS by default, no server-side latency. The issues that remain are configuration, and most of them come down to the same root cause: several URLs serving the same content.
Table of contents
- Stop preview deployments being indexed
- Redirects, and the status codes that matter
- Trailing slashes and duplicate URLs
- Headers worth setting
- The parts the platform cannot do for you
- How this fits the rest of the stack
- FAQ
Stop preview deployments being indexed
This is the most consequential and the most overlooked. Every branch and pull request gets its own URL, and those URLs are publicly accessible. If they get indexed, you have duplicate content competing with your production site, and search engines have to decide which is canonical.
The platform sets X-Robots-Tag: noindex on deploy previews and branch deploys by default, which covers the common case. Verify rather than assume, especially on older sites or custom configurations:
curl -sI https://deploy-preview-42--yoursite.netlify.app | grep -i robots
# x-robots-tag: noindex
Where that header is absent, add it explicitly for non-production contexts:
# netlify.toml
[[context.deploy-preview.headers]]
for = "/*"
[context.deploy-preview.headers.values]
X-Robots-Tag = "noindex, nofollow"
[[context.branch-deploy.headers]]
for = "/*"
[context.branch-deploy.headers.values]
X-Robots-Tag = "noindex, nofollow"
The related issue: the default *.netlify.app subdomain remains live after you attach a custom domain, serving identical content. Redirect it so there is one address for your site:
[[redirects]]
from = "https://yoursite.netlify.app/*"
to = "https://www.example.com/:splat"
status = 301
force = true
force = true matters — without it the redirect does not apply where a matching file exists, which is every page on a static site.
Redirects, and the status codes that matter
Redirects go in netlify.toml or a _redirects file. The status code carries the SEO meaning and getting it wrong loses ranking signals.
# _redirects
/old-page /new-page 301
/blog/2023/* /blog/:splat 301
/temporary-promo /promo-2026 302
/api/* https://api.example.com/:splat 200
/* /index.html 200
- 301 — permanent. Passes ranking signals to the new URL. Use this for anything genuinely moved.
- 302 — temporary. Search engines keep the original URL indexed. Using 302 for a permanent move is a common and costly mistake.
- 200 — a rewrite. The URL in the browser does not change and the content is served from elsewhere. This is what SPA fallback uses.
- 404 — for content that is genuinely gone with no replacement. Redirecting everything to the homepage instead is worse, and search engines treat it as a soft 404.
Order matters — the first matching rule wins, so put specific rules above catch-alls. The SPA fallback /* /index.html 200 must be last, or it swallows everything below it.
One thing to watch on SPA sites: that catch-all returns 200 for URLs that do not exist, so a genuinely missing page is indexed as a valid one. Handle unknown routes by rendering a page with a noindex tag, or prerender real 404s where your framework supports it.
Trailing slashes and duplicate URLs
/about and /about/ can serve the same content at two URLs. Search engines treat them as different pages unless told otherwise, which splits ranking signals between them.
The platform has a Pretty URLs setting that redirects to a consistent form. Confirm which form your site actually settles on:
curl -sI https://example.com/about | head -1
curl -sI https://example.com/about/ | head -1
# one should be a 301 to the other, not both returning 200
Pick one convention, make sure the redirect enforces it, and then make everything else agree with it:
- Internal links throughout the site.
- The canonical tag on every page.
- The URLs in your sitemap.
- Any hardcoded URLs in structured data.
The same reasoning applies to www versus apex. Choose one as primary and 301 the other — the platform does this when you set a primary domain, and it is worth verifying with curl rather than assuming.
And the canonical tag, which is the backstop for all of this:
<link rel="canonical" href="https://www.example.com/about/" />
Generate it from the site’s configured production URL, not from the request. A canonical built from the current hostname points preview deployments at themselves, which is exactly the outcome the noindex header was preventing.
Headers worth setting
Caching is the one with a direct performance effect. Fingerprinted assets can be cached indefinitely; HTML cannot:
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.html"
[headers.values]
Cache-Control = "public, max-age=0, must-revalidate"
Getting this backwards — caching HTML aggressively — means content updates do not reach users, which is a worse problem than a slightly slower load.
Security headers, which are a ranking factor only indirectly but are worth having:
[[headers]]
for = "/*"
[headers.values]
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Strict-Transport-Security = "max-age=31536000; includeSubDomains"
Be careful with HSTS — it is difficult to reverse, since browsers remember it for the max-age you specified. Start with a short duration and increase once you are confident HTTPS is working everywhere.
Verify what is actually being sent, since header configuration is easy to get subtly wrong:
curl -sI https://example.com/assets/app.a1b2c3.js | grep -i cache-control
The parts the platform cannot do for you
Worth being clear about, because platform-focused SEO advice tends to imply hosting is most of the job. It is not.
- A sitemap that reflects reality. Generate it at build time from your actual routes, reference it in
robots.txt, and keep the URL forms consistent with your canonical convention. - Titles and meta descriptions per page. Unique, descriptive, and not truncated. This is content work.
- Structured data where it applies — Article, Product, FAQPage, BreadcrumbList. Validate it, since malformed JSON-LD fails silently.
- Internal linking. Descriptive anchor text, and no links to pages that do not exist. A broken internal link is invisible until someone clicks it.
- Content that answers the query. No amount of configuration substitutes for this.
A build-time link check is worth adding to CI. Broken internal links accumulate quietly on static sites because nothing errors at build time — the page simply 404s for whoever clicks it.
The realistic split: hosting configuration prevents problems, and content earns rankings. Both matter, and only one of them is a settings file.
How this fits the rest of the stack
Nearly every hosting-level SEO problem reduces to the same thing — the same content available at more than one URL, or content available where it should not be. Preview deployments, apex versus www, trailing slashes and stale platform subdomains are four versions of one issue, and the fix in each case is a redirect and a canonical that agree with each other.
RunxBuild static sites support redirects, rewrites, custom response headers, SPA fallback and custom domains with certificates, with 120GB bandwidth included and $0.10/GB after — so the same controls are available and configured per site. If the site grows a backend, the service and a managed database run on the same platform rather than as a separate arrangement. The RunxBuild hosting calculator shows the bandwidth and service costs as separate line items.
Useful related references:
- Netlify Forms: What You Get Free, and Where the Ceiling Is
- Domain Forwarding and SEO: Redirects That Keep Your Rankings
- Netlify Drop: The Fastest Way to Put a Folder on the Internet
- Services on RunxBuild
FAQ
Do Netlify deploy previews hurt SEO?
Only if they get indexed, which would create duplicate content competing with production. Preview and branch deploys carry an X-Robots-Tag: noindex header by default — verify it with curl -sI on a preview URL, and set it explicitly per context in netlify.toml if it is missing.
Should I use 301 or 302 redirects?
301 for anything permanently moved, because it passes ranking signals to the new URL. 302 only for genuinely temporary changes, since search engines keep the original URL indexed. Using 302 for a permanent move is a common mistake that costs ranking.
How do I handle trailing slashes for SEO?
Pick one convention and enforce it with a redirect, then make internal links, canonical tags and your sitemap all agree with it. Check with curl -sI on both forms — one should 301 to the other rather than both returning 200.
Does my netlify.app subdomain cause duplicate content?
It can, since it stays live and serves identical content after you attach a custom domain. Add a 301 redirect from the subdomain to your primary domain with force = true, which is required for the rule to apply when a matching file exists.
What should I set for Cache-Control on a static site?
Long and immutable for fingerprinted assets — max-age=31536000, immutable — and max-age=0, must-revalidate for HTML. Getting it backwards means content updates never reach users, which is worse than a marginally slower first load.