NetcaneTechnologies

SEO

Technical SEO for Developers: What Actually Moves Rankings

The technical SEO decisions that live in code, not in a marketing checklist — rendering strategy, canonical implementation, redirect architecture, and the Core Web Vitals work that's actually engineering, not configuration.

Yasir Haleem7 min read

Most technical SEO content is written for marketers and reads like a checklist — set a meta description, submit a sitemap, add alt text. Useful, but it skips the layer that's actually an engineering decision, not a configuration toggle: how your rendering strategy affects crawlability, how canonical logic gets implemented correctly in code rather than hoped into existence by a plugin, and where Core Web Vitals work is genuinely architecture, not a settings panel. This is the developer-facing half of technical SEO — the part that determines whether the checklist items even have a chance of working.

Rendering strategy is the foundational decision

Search engines crawl what your server actually returns, and for Google specifically, JavaScript execution during crawling is real but not free, fast, or guaranteed to behave identically to a browser — client-rendered content is a second-class citizen relative to content that's in the initial HTML response.

Server-rendered or statically generated HTML is the safe default for anything that needs to rank — a Next.js app using Server Components, static generation, or ISR delivers a complete HTML document on first response, no dependence on crawl-time JavaScript execution. A fully client-rendered SPA can still get indexed, but you're trusting a rendering pipeline you don't control and can't easily debug when something goes wrong.

The practical implication for a Next.js app: keep the primary content — the text, headings, and links that matter for a given page — in Server Components, and reserve Client Components ("use client") for genuinely interactive pieces. This isn't just a performance best practice; it's what guarantees the content search engines need is present in the HTML they actually fetch, rather than requiring script execution to appear.

Canonical tags: implement them as code, not as an afterthought

A canonical tag is a claim your codebase makes about which URL is the authoritative version of a piece of content — and it needs to be generated correctly and consistently, not set once by a plugin and forgotten. In Next.js's App Router, this is the alternates.canonical field in the Metadata object, generated per-route:

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    alternates: {
      canonical: `https://example.com/blog/${post.slug}`,
    },
  };
}

The bug that actually happens in practice: canonical URLs generated from request.url or the current path rather than a known-good, normalized value — which means a URL with a stray query parameter (a UTM tag, a session ID) gets set as its own canonical instead of canonicalizing to the clean version. Generate canonicals from your data layer (the slug or ID you already have), never from the incoming request's raw URL, so tracking parameters and URL variations can never leak into the canonical tag.

Redirects: architecture, not a spreadsheet

A redirect map matters most during a migration, but the architecture of how redirects are implemented is a developer decision with real performance and correctness implications. Next.js supports redirects at the next.config level (fast, edge-evaluated, ideal for a known, static set) and at the middleware level (more flexible, can be data-driven, but adds a function invocation to the request path).

Avoid redirect chains — URL A redirecting to B redirecting to C — which cost an extra round trip per hop and are a common accumulation artifact after multiple migrations. Periodically audit your redirect list for chains and collapse them to a single hop from the original URL directly to the current one. Always use permanent (301/308) redirects for genuinely permanent moves — a temporary (302) redirect on a URL that's actually moved forever tells search engines not to transfer ranking signal to the destination, which quietly undermines the exact thing a redirect is supposed to preserve.

Sitemaps and robots: generate them from real data, not a static file

A hand-maintained sitemap.xml drifts from reality within weeks on any site with regularly published content. In Next.js, app/sitemap.ts generates the sitemap dynamically from your actual data source (CMS entries, dynamic routes) at build or request time — this is the version that stays accurate without someone remembering to update a static file.

// app/sitemap.ts
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts();
  return [
    { url: "https://example.com", lastModified: new Date() },
    ...posts.map((post) => ({
      url: `https://example.com/blog/${post.slug}`,
      lastModified: post.updatedAt,
    })),
  ];
}

Similarly, app/robots.ts generates robots.txt programmatically — worth doing even for simple rules, since it keeps the sitemap reference and disallow paths in one place that's part of your codebase and reviewed like any other code change, rather than a file that gets edited directly on a server somewhere and nobody remembers why.

Core Web Vitals: the parts that are genuinely engineering

Performance-as-SEO gets treated as a checklist item ("optimize images") when a meaningful share of it is architecture:

  • LCP often comes down to whether your largest content element is server-rendered and prioritized, or waits on client-side data fetching after hydration. A hero image or heading that only appears after a client-side useEffect fetch is a self-inflicted LCP penalty that no image compression fixes.
  • INP is a direct function of main-thread JavaScript — bundle size, third-party scripts, and how much client-side interactivity you've shipped versus how much could have stayed server-rendered. This is a code-splitting and Client Component boundary decision, not a settings toggle.
  • CLS is almost always a layout decision: reserving space for images (explicit dimensions), avoiding content that injects above already-rendered content (a late-loading banner pushing the page down), and treating font loading deliberately (font-display: swap, self-hosted fonts) so text doesn't reflow when a webfont finishes loading.

None of these are "add a plugin" fixes — they're decisions made in component architecture, and they're the reason a technically well-built Next.js app tends to out-rank a plugin-optimized WordPress site on Core Web Vitals without any SEO-specific effort at all, purely as a byproduct of the rendering model.

Structured data: generate it from the same data, don't hand-maintain a duplicate

JSON-LD should be generated from the same data that renders the visible page — the same title, the same date, the same author — not maintained as a separate hand-written block that drifts out of sync with the actual content. Our structured data patterns guide covers the specific schema types and validation approach; the developer-relevant point here is architectural: build a small set of schema-generating functions (getArticleJsonLd(post), getFAQJsonLd(faqs)) that take your actual data models as input, so structured data can never silently diverge from the page it describes.

The developer's actual checklist

LayerWhat's actually an engineering decision
RenderingServer Components/SSG for anything that needs to rank; Client Components scoped narrowly
CanonicalsGenerated from data (slug/ID), never from raw request URL
RedirectsNo chains, correct status codes (301 for permanent), config-level where possible
Sitemap/robotsGenerated dynamically from real data (app/sitemap.ts), not hand-maintained files
Core Web VitalsComponent architecture and bundle discipline, not a plugin or a settings panel
Structured dataGenerated from the same data models as the visible page, never hand-duplicated

Where this fits with the rest of technical SEO

None of this replaces the marketing-facing checklist — unique titles, meta descriptions, alt text, a redirect plan for a migration all still matter, and our SEO launch checklist and on-page SEO guide cover that ground. What this layer does is make sure the checklist items have a foundation to work on — a canonical tag configured correctly by a plugin still fails if the rendering strategy underneath it serves different content to crawlers than to users.

We build this in from the architecture stage on every Next.js and headless CMS project, not bolted on after a marketing audit flags an issue. If your site's technical SEO checklist looks complete but rankings still lag, the gap is often in this layer — get in touch if you want a developer-level audit rather than another checklist.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.