Web razvoj
Next.jsSEOTehnički SEOApp RouterPerformanseCore Web VitalsStrukturirani podaci

Kontrolni popis za tehnički SEO audit u Next.js-u (App Router): indeksiranje, metapodaci, Core Web Vitals i strukturirani podaci

AO
Adrijan Omićević
·15 min čitanja

# Što ćete naučiti#

Ovaj vodič je praktičan kontrolni popis za tehnički SEO audit u Next.js-u za projekte koji koriste Next.js App Router. Svaki korak objašnjava što provjeriti, kako implementirati ili popraviti, i kako to utječe na crawlanje, indeksiranje i performanse.

Ako prvo želite stratešku pozadinu, pročitajte Zašto Next.js za SEO, a zatim se vratite na ovaj popis za provedbu.

# Preduvjeti i alati#

AlatZašto vam trebaŠto tražiti
Google Search ConsoleSignali indeksiranja i kanonikalaPokrivenost, sitemapovi, URL Inspection, rich results
Lighthouse ili PageSpeed InsightsLab performanseLCP, CLS, INP, TTFB, render-blocking
Chrome DevTools PerformanceDebug runtime problemaDugi taskovi, layout shiftovi, cijena hidratacije
CurlBrze provjere headera i robotsaStatus kodovi, caching, X-Robots-Tag
Structured Data TestingValidacija JSON-LD-aGreške, upozorenja, eligibility

# Korak 1: Potvrdite strategiju renderiranja po ruti#

Prvi SEO rizik u Next.js-u je nenamjerno isporučivanje stranica koje izgledaju dobro korisnicima, ali su teže za crawlanje ili indeksiranje jer je ključni sadržaj zaključan iza client-only renderiranja ili odgođenog dohvaćanja podataka.

Kontrolni popis#

ProvjeraKriterij prolazaZašto je važno
Marketinške stranice renderiraju se na serveruHTML sadrži primarni sadržaj bez JS-aBotovi i social crawleri odmah dobivaju sadržaj
Dinamičke stranice izbjegavaju client-only “shell”Nema praznog diva s sadržajem tek nakon hidratacijeSprječava tanak HTML i kašnjenja renderiranja
Status kodovi su ispravni200, 301, 404, 410 prema očekivanjuIndeksiranje ovisi o ispravnoj HTTP semantici

Brza provjera#

  1. 1
    Otvorite stranicu.
  2. 2
    Pogledajte izvorni kod (View page source).
  3. 3
    Potvrdite da su glavni naslovi i tekst prisutni u HTML-u.

🎯 Ključna poruka: Ako vaš primarni sadržaj nedostaje u inicijalnom HTML-u, oslanjate se na Google da renderira JS. To povećava trošak crawlanja i odgađa indeksiranje.

# Korak 2: Audit signala indeksabilnosti (Meta robots i X-Robots-Tag)#

Problemi s indeksiranjem često su samonaneseni: slučajni noindex, nofollow ili blokirane putanje.

Što provjeriti#

PovršinaGdje se može postavitiTipičan kvar
Meta robotsMetadata API ili ručni meta tagovinoindex primijenjen globalno u layoutu
X-Robots-Tag headerReverse proxy, CDN, middleware, route handleriStaging header “procuri” u produkciju
Robots.txtapp/robots.ts ili statička datotekaZabranjivanje važnih sekcija

Implementirajte robots direktive pomoću Metadata API-ja#

TypeScript
// app/(marketing)/layout.tsx
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  robots: {
    index: true,
    follow: true,
    googleBot: {
      index: true,
      follow: true,
      'max-snippet': -1,
      'max-image-preview': 'large',
      'max-video-preview': -1,
    },
  },
};

Dodajte noindex samo za ne-javne dijelove#

TypeScript
// app/(app)/account/layout.tsx
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  robots: {
    index: false,
    follow: false,
  },
};

Provjera na razini headera s curlom#

Bash
curl -I https://example.com/

Tražite nenamjerni x-robots-tag: noindex.

⚠️ Upozorenje: Jedan globalni robots: { index: false } na razini layouta može deindeksirati cijeli site, čak i ako su robots.txt i sitemap ispravni.

# Korak 3: Kanonikalni URL-ovi i kontrole duplikata sadržaja#

Kanonikali su način na koji tražilicama govorite koji je URL “glavna” verzija kada više URL-ova može prikazivati isti sadržaj.

U Next.js-u se duplikacija najčešće događa zbog:

  • varijanti s trailing slashom
  • query parametara
  • paginacijskih stranica
  • locale ruta
  • više putanja kategorija koje vode na isti item

Kanonikalni kontrolni popis#

ProvjeraKriterij prolazaUčinak
Svaka indeksabilna stranica ima self canonicalCanonical odgovara preferiranom URL-uKonsolidira ranking signale
Canonical pokazuje na 200 indeksabilan URLNema kanonikala na 404 ili redirectan URLSprječava ignoriranje kanonikala
Parametri ne stvaraju duple kanonikaleCanonical uklanja tracking parametreIzbjegava dijeljenje autoriteta

Implementirajte canonical s metadataBase i alternates.canonical#

TypeScript
// app/layout.tsx
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  metadataBase: new URL('https://example.com'),
};
TypeScript
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
 
export async function generateMetadata(
  props: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
  const { slug } = await props.params;
 
  return {
    title: `Post: ${slug}`,
    alternates: {
      canonical: `/blog/${slug}`,
    },
  };
}

Normalizirajte redirecte za trailing slash#

Ovo napravite na edgeu ili u pravilima platforme. U Next.js-u možete koristiti i redirects.

JavaScript
// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/:path*/',
        destination: '/:path*',
        permanent: true,
      },
    ];
  },
};

💡 Savjet: Kanonikale tretirajte kao test dosljednosti: ako vaša logika kanonikala ovisi o request headerima ili query stringovima, prije ili kasnije ćete generirati konfliktne kanonikale i zbuniti indeksiranje.

# Korak 4: Audit pokrivenosti metapodataka (naslovi, opisi, Open Graph)#

Nedostajući ili duplicirani metapodaci smanjuju CTR i mogu oslabiti signale relevantnosti. S App Routerom koristite generateMetadata kako bi svaka dinamička stranica imala determinističke metapodatke.

Minimalni kontrolni popis metapodataka#

ElementPreporukaZašto je važno
TitleJedinstven, usklađen s ključnim riječima, 50 do 60 znakovaCTR i tematska relevantnost
Meta descriptionJedinstven, 140 do 160 znakovaCTR i kontrola snippeta
Open GraphTitle, description, image, URLSocial previewi, dijeljenje
Twitter cardsummary_large_imageDosljedni previewi

Primjer: dinamički metapodaci s Open Graphom#

TypeScript
// app/products/[id]/page.tsx
import type { Metadata } from 'next';
 
export async function generateMetadata(
  props: { params: Promise<{ id: string }> }
): Promise<Metadata> {
  const { id } = await props.params;
 
  const product = await fetch(`https://api.example.com/products/${id}`, {
    cache: 'no-store',
  }).then((r) => r.json());
 
  return {
    title: `${product.name} | Example Store`,
    description: product.shortDescription,
    alternates: { canonical: `/products/${id}` },
    openGraph: {
      title: `${product.name} | Example Store`,
      description: product.shortDescription,
      url: `/products/${id}`,
      images: [
        { url: product.ogImageUrl, width: 1200, height: 630, alt: product.name },
      ],
      type: 'product',
    },
    twitter: {
      card: 'summary_large_image',
      title: `${product.name} | Example Store`,
      description: product.shortDescription,
      images: [product.ogImageUrl],
    },
  };
}

Ako vam treba šira SEO osnova kroz timove, SEO za developere je dobar temelj za usklađivanje odluka u kodu i sadržaju.

# Korak 5: Robots.txt i XML Sitemap (nativno za App Router)#

Robots.txt utječe na crawlanje. Sitemapovi utječu na otkrivanje (discovery) i učinkovitost crawlanja. Oboje direktno utječe na to koliko brzo se nove stranice indeksiraju i koliko se dosljedno veliki siteovi ponovno posjećuju.

Implementirajte robots.txt u App Routeru#

TypeScript
// app/robots.ts
import type { MetadataRoute } from 'next';
 
export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/account', '/checkout', '/api'],
      },
    ],
    sitemap: 'https://example.com/sitemap.xml',
  };
}

Implementirajte sitemap.xml u App Routeru#

TypeScript
// app/sitemap.ts
import type { MetadataRoute } from 'next';
 
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await fetch('https://api.example.com/posts', {
    next: { revalidate: 3600 },
  }).then((r) => r.json());
 
  return [
    { url: 'https://example.com/', lastModified: new Date() },
    ...posts.map((p: { slug: string; updatedAt: string }) => ({
      url: `https://example.com/blog/${p.slug}`,
      lastModified: new Date(p.updatedAt),
    })),
  ];
}

Kontrolni popis za audit sitemapa#

ProvjeraKriterij prolazaUčinak
Sitemap vraća 200Nema autha, nema caching grešakaGoogle ga može pouzdano dohvatiti
URL-ovi su apsolutniPuni https URL-oviSprječava dvosmislenost pri parsiranju
Uključeni su samo kanonikalni URL-oviNema varijanti s parametrimaPoboljšava učinkovitost crawl budgeta
lastModified je realističanOdgovara stvarnim izmjenamaBolji timing ponovnog crawlanja

ℹ️ Napomena: Google ne jamči da će koristiti lastmod, ali točne vremenske oznake koreliraju s pametnijim recrawlanjem na velikim siteovima.

# Korak 6: Paginacija i facet filteri (spriječite crawl trapove)#

Paginacija i filteri mogu eksplodirati u tisuće URL-ova. To može razvodniti crawl budget i stvoriti klastere dupliciranog sadržaja.

Kontrolni popis za paginaciju#

ProvjeraKriterij prolazaUčinak
Paginacijske stranice su indeksabilne samo ako vrijedeStranica 1 indeksabilna, dublje stranice ovisno o slučajuIzbjegava “thin” napuhavanje indeksa
Strategija kanonikala je dosljednaSelf-canonical po stranici ili canonical na stranicu 1Konsolidira ili čuva long-tail
Interni linkovi su crawlablePravi linkovi, ne samo JS handleriDiscovery i crawl putanje

Implementirajte paginiranu rutu s dosljednim kanonikalima#

TypeScript
// app/blog/page/[page]/page.tsx
import type { Metadata } from 'next';
 
export async function generateMetadata(
  props: { params: Promise<{ page: string }> }
): Promise<Metadata> {
  const { page } = await props.params;
  const pageNum = Number(page);
 
  return {
    title: `Blog - Stranica ${pageNum}`,
    alternates: {
      canonical: `/blog/page/${pageNum}`,
    },
  };
}

Spriječite indeksiranje beskonačnih kombinacija filtera#

Ako imate facet URL-ove poput /search?color=red&size=m, ili:

  • blokirajte crawlanje preko robots.txt za uzorak putanja s parametrima gdje je moguće, ili
  • ostavite crawlable, ali postavite noindex za kombinacije niske vrijednosti.

Pragmatično pravilo: indeksirajte samo filtere s potražnjom u pretraživanju i stabilnim inventarom.

⚠️ Upozorenje: Blokiranje preko robots.txt sprječava crawlanje, ne indeksiranje, kada postoje vanjski linkovi. Ako je URL već otkriven, noindex je pouzdana direktiva za deindeksiranje, ali zahtijeva da stranica bude crawlable.

# Korak 7: HTTP status kodovi, redirecti i error stranice#

Google redirecte i greške tretira kao snažne signale. Next.js site s pogrešnim 200 odgovorima za nepostojeće stranice nakupit će soft 404 i trošiti crawl.

Kontrolni popis#

ProvjeraKriterij prolazaUčinak
404 stranice vraćaju pravi 404Ne 200 s tekstom “Not found”Sprječava soft 404 klasifikaciju
Izbjegnuti su redirect lanciNajviše jedan hop za česte URL-oveBrže crawlanje, manje gubitka signala
410 za trajno uklonjen sadržajOpcionalno, ali korisnoU praksi brže deindeksiranje nego 404

App Router 404 obrada#

Koristite notFound() za zapise koji nedostaju.

TypeScript
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
 
export default async function Page(
  props: { params: Promise<{ slug: string }> }
) {
  const { slug } = await props.params;
 
  const res = await fetch(`https://api.example.com/posts/${slug}`);
  if (res.status === 404) notFound();
 
  const post = await res.json();
  return <main><h1>{post.title}</h1></main>;
}

Redirecte držite centralizirane i stabilne.

JavaScript
// next.config.js
module.exports = {
  async redirects() {
    return [
      { source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
    ];
  },
};

# Korak 8: Audit Core Web Vitals (LCP, INP, CLS) za App Router#

Performanse su sada neodvojive od SEO-a. Googleove Core Web Vitals metrike evaluiraju se na podacima stvarnih korisnika, tipično na 75. percentilu.

Ciljevi:

  • LCP <= 2.5s
  • INP <= 200ms
  • CLS <= 0.1

Za detaljniji performance playbook, pogledajte Optimizacija performansi web stranice.

Što provjeriti specifično u Next.js-u#

MetrikaNajčešći Next.js uzrociPopravci koji stvarno pomiču metrike
LCPNeoptimizirana hero slika, spor TTFB, blokirajući fontoviNext Image, caching, streaming SSR, optimizacija fontova
INPTeška hidratacija, veliki client bundleovi, skupi event handleriPrebacite logiku na server, smanjite JS, code split, koristite Server Components
CLSSlike bez dimenzija, dinamički ubacivani banneri, font swapoviEksplicitne dimenzije, stabilan layout, font-display strategija

LCP: popravite hero sliku i priority#

TSX
// app/(marketing)/page.tsx
import Image from 'next/image';
 
export default function Home() {
  return (
    <main>
      <Image
        src="/images/hero.jpg"
        alt="Product screenshot"
        width={1600}
        height={900}
        priority
        sizes="(max-width: 768px) 100vw, 1200px"
      />
      <h1>Brze, SEO-friendly Next.js web stranice</h1>
    </main>
  );
}

INP: smanjite client komponente i površinu hidratacije#

Auditirajte gdje koristite "use client". U App Routeru mnogi UI dijelovi po defaultu mogu biti Server Components, što može smanjiti količinu JS-a isporučenog u browser.

Jednostavno pravilo: ako ne treba browser-only API-je, nemojte ga raditi kao client component.

CLS: rezervirajte prostor za dinamičke elemente#

Ako ubacujete bannere ili consent UI, rezervirajte prostor kako biste izbjegli pomake. Također osigurajte da sve slike imaju eksplicitne dimenzije.

💡 Savjet: Mjerite preko real-user monitoringa. Lab alati su dobri za debug, ali samo field podaci govore zadovoljavaju li korisnici na 75. percentilu LCP i INP pragove.

# Korak 9: Caching i revalidacija (performanse i učinkovitost crawlanja)#

Brzi odgovori poboljšavaju UX i omogućuju tražilicama crawlanje više URL-ova po jedinici vremena. Loš caching često se vidi kao visok TTFB i spor LCP.

Kontrolni popis#

ProvjeraKriterij prolazaUčinak
Statično gdje god je mogućeMarketinške i evergreen stranice prerenderiraneBrže indeksiranje i bolji CWV
Revalidacija je namjernarevalidate usklađen sa svježinom sadržajaIzbjegava zastarjele SERP snippete
CDN caching nije “slomljen”Cache headeri su dosljedniSmanjuje opterećenje origina i ubrzava botove

Primjer: revalidirajte sadržaj svaki sat#

TypeScript
// app/blog/[slug]/page.tsx
export const revalidate = 3600;

Primjer: fetch caching usklađen s revalidacijom#

TypeScript
// app/blog/[slug]/page.tsx
const post = await fetch(`https://api.example.com/posts/${slug}`, {
  next: { revalidate: 3600 },
}).then((r) => r.json());

# Korak 10: Audit i implementacija strukturiranih podataka (JSON-LD)#

Strukturirani podaci poboljšavaju eligibility za rich rezultate i smanjuju dvosmislenost oko entiteta poput članaka, proizvoda, breadcrumbova i organizacija. Ne jamče rich rezultate, ali povećavaju šanse kada se sadržaj i politike poklapaju.

Kontrolni popis za schema#

Schema tipGdje pomažeMinimalna polja za validaciju
OrganizationZnanje o brendu i povjerenjename, url, logo
WebSite + SearchActionEligibility za sitelinks search boxurl, potentialAction
BreadcrumbListBreadcrumb rich rezultatiitemListElement
ArticleArticle rich rezultatiheadline, image, datePublished, author
ProductProduct rich rezultatiname, offers, aggregateRating ako postoji

Sigurno dodajte JSON-LD u App Routeru#

U MDX-u i JSX-u scriptovi su dopušteni, ali držite ih minimalnima i valjanima. Najjednostavniji pristup je renderirati script tag s application/ld+json.

TSX
// app/blog/[slug]/StructuredData.tsx
export function ArticleJsonLd(props: {
  url: string;
  title: string;
  imageUrl: string;
  datePublished: string;
  dateModified: string;
}) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    mainEntityOfPage: props.url,
    headline: props.title,
    image: [props.imageUrl],
    datePublished: props.datePublished,
    dateModified: props.dateModified,
    author: [{ '@type': 'Person', name: 'Adrijan Omićević' }],
    publisher: {
      '@type': 'Organization',
      name: 'Samioda',
      logo: {
        '@type': 'ImageObject',
        url: 'https://example.com/logo.png',
      },
    },
  };
 
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
    />
  );
}

Zatim ga uključite na stranici:

TSX
// app/blog/[slug]/page.tsx
import { ArticleJsonLd } from './StructuredData';
 
export default async function Page(
  props: { params: Promise<{ slug: string }> }
) {
  const { slug } = await props.params;
  const post = await fetch(`https://api.example.com/posts/${slug}`).then((r) => r.json());
 
  return (
    <main>
      <ArticleJsonLd
        url={`https://example.com/blog/${slug}`}
        title={post.title}
        imageUrl={post.ogImageUrl}
        datePublished={post.publishedAt}
        dateModified={post.updatedAt}
      />
      <h1>{post.title}</h1>
      <article>{post.content}</article>
    </main>
  );
}

Primjer breadcrumb scheме#

TSX
// app/components/BreadcrumbJsonLd.tsx
export function BreadcrumbJsonLd(props: {
  items: Array<{ name: string; url: string }>;
}) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    itemListElement: props.items.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.url,
    })),
  };
 
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
    />
  );
}

# Korak 11: Interno linkanje i dubina crawlanja#

Čak i uz savršene sitemapove, interni linkovi pokreću većinu discoveryja i signala prioriteta. Stranice dublje od ~3 klika često se crawla rjeđe na srednje velikim siteovima.

Kontrolni popis#

ProvjeraKriterij prolazaUčinak
Postoje kategorijske i hub straniceJasni tematski hubovi s linkovima na ključne straniceBolji discovery i relevantnost
Linkovi su stvarni anchor linkoviNe samo click handleriCrawlable navigacija
Nema orphan stranicaSvaki indeksabilan URL je interno linkanSprječava slabo otkrivanje i slabe signale

Koristite server-renderirane navigacijske komponente i osigurajte da su linkovi standardni Next.js Link s pravim href vrijednostima.

# Korak 12: Audit dosljednosti outputa između okruženja#

Čest SEO bug koji se pojavljuje samo u produkciji je “environment drift”: staging se razlikuje od produkcije u indeksiranju, kanonikalnim hostnameovima ili robots direktivama.

Kontrolni popis#

ProvjeraKriterij prolazaUčinak
metadataBase koristi produkcijsku domenuNema staging URL-ova u kanonikalima ili OG URL-ovimaSprječava pogrešnu kanonikalnu konsolidaciju
robots pravila se razlikuju kako trebaStaging je blokiran, produkcija je otvorenaIzbjegava indeksiranje staginga
Sitemapovi navode samo produkcijske URL-oveNema miješanih hostovaSprječava rasipanje crawlanja i konfuziju

Ako varijable okruženja mogu utjecati na metapodatke, “zaključajte” ih i validirajte automatiziranim provjerama u CI-ju.

# Ključne poruke#

  • Indeksabilnost tretirajte kao feature prve klase: auditirajte robots, meta robots i X-Robots-Tag kako biste spriječili slučajno deindeksiranje.
  • Generirajte determinističke metapodatke i kanonikale pomoću App Router Metadata API-ja, posebno za dinamičke rute i paginaciju.
  • Isporucite App Router-native robots.ts i sitemap.ts i držite sitemapove strogo kanonikalnima kako biste poboljšali discovery i učinkovitost crawlanja.
  • Optimizirajte Core Web Vitals: Next Image za LCP, manje client komponenti za INP i stabilno dimenzioniranje layouta za CLS.
  • Dodajte JSON-LD schemu za tipove sadržaja koje objavljujete i validirajte u Search Consoleu kako biste povećali eligibility za rich rezultate.

# Zaključak#

Dobar kontrolni popis za tehnički SEO audit u Next.js-u najviše se svodi na dosljednost: dosljedni signali indeksabilnosti, dosljedni kanonikali, dosljedni metapodaci i dosljedne performanse. Ako implementirate gore navedene korake, smanjit ćete rasipanje crawlanja, ubrzati indeksiranje i zaštititi rangiranja od regresija tijekom releaseova.

Ako želite da Samioda odradi ovaj audit na vašem Next.js App Router projektu i isporuči popravke, kontaktirajte nas putem naše stranice i pripremit ćemo prioritetni backlog vezan uz utjecaj na indeksiranje i dobitke u Core Web Vitals metrikama.

FAQ

Share
A
Adrijan OmićevićSamioda Team
All articles →

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.