# Š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#
| Alat | Zašto vam treba | Što tražiti |
|---|---|---|
| Google Search Console | Signali indeksiranja i kanonikala | Pokrivenost, sitemapovi, URL Inspection, rich results |
| Lighthouse ili PageSpeed Insights | Lab performanse | LCP, CLS, INP, TTFB, render-blocking |
| Chrome DevTools Performance | Debug runtime problema | Dugi taskovi, layout shiftovi, cijena hidratacije |
| Curl | Brze provjere headera i robotsa | Status kodovi, caching, X-Robots-Tag |
| Structured Data Testing | Validacija JSON-LD-a | Greš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#
| Provjera | Kriterij prolaza | Zašto je važno |
|---|---|---|
| Marketinške stranice renderiraju se na serveru | HTML sadrži primarni sadržaj bez JS-a | Botovi i social crawleri odmah dobivaju sadržaj |
| Dinamičke stranice izbjegavaju client-only “shell” | Nema praznog diva s sadržajem tek nakon hidratacije | Sprječava tanak HTML i kašnjenja renderiranja |
| Status kodovi su ispravni | 200, 301, 404, 410 prema očekivanju | Indeksiranje ovisi o ispravnoj HTTP semantici |
Brza provjera#
- 1Otvorite stranicu.
- 2Pogledajte izvorni kod (View page source).
- 3Potvrdite 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šina | Gdje se može postaviti | Tipičan kvar |
|---|---|---|
| Meta robots | Metadata API ili ručni meta tagovi | noindex primijenjen globalno u layoutu |
| X-Robots-Tag header | Reverse proxy, CDN, middleware, route handleri | Staging header “procuri” u produkciju |
| Robots.txt | app/robots.ts ili statička datoteka | Zabranjivanje važnih sekcija |
Implementirajte robots direktive pomoću Metadata API-ja#
// 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#
// 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#
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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| Svaka indeksabilna stranica ima self canonical | Canonical odgovara preferiranom URL-u | Konsolidira ranking signale |
| Canonical pokazuje na 200 indeksabilan URL | Nema kanonikala na 404 ili redirectan URL | Sprječava ignoriranje kanonikala |
| Parametri ne stvaraju duple kanonikale | Canonical uklanja tracking parametre | Izbjegava dijeljenje autoriteta |
Implementirajte canonical s metadataBase i alternates.canonical#
// app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
};// 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.
// 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#
| Element | Preporuka | Zašto je važno |
|---|---|---|
| Title | Jedinstven, usklađen s ključnim riječima, 50 do 60 znakova | CTR i tematska relevantnost |
| Meta description | Jedinstven, 140 do 160 znakova | CTR i kontrola snippeta |
| Open Graph | Title, description, image, URL | Social previewi, dijeljenje |
| Twitter card | summary_large_image | Dosljedni previewi |
Primjer: dinamički metapodaci s Open Graphom#
// 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#
// 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#
// 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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| Sitemap vraća 200 | Nema autha, nema caching grešaka | Google ga može pouzdano dohvatiti |
| URL-ovi su apsolutni | Puni https URL-ovi | Sprječava dvosmislenost pri parsiranju |
| Uključeni su samo kanonikalni URL-ovi | Nema varijanti s parametrima | Poboljšava učinkovitost crawl budgeta |
| lastModified je realističan | Odgovara stvarnim izmjenama | Bolji 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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| Paginacijske stranice su indeksabilne samo ako vrijede | Stranica 1 indeksabilna, dublje stranice ovisno o slučaju | Izbjegava “thin” napuhavanje indeksa |
| Strategija kanonikala je dosljedna | Self-canonical po stranici ili canonical na stranicu 1 | Konsolidira ili čuva long-tail |
| Interni linkovi su crawlable | Pravi linkovi, ne samo JS handleri | Discovery i crawl putanje |
Implementirajte paginiranu rutu s dosljednim kanonikalima#
// 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
noindexza 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,
noindexje 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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| 404 stranice vraćaju pravi 404 | Ne 200 s tekstom “Not found” | Sprječava soft 404 klasifikaciju |
| Izbjegnuti su redirect lanci | Najviše jedan hop za česte URL-ove | Brže crawlanje, manje gubitka signala |
| 410 za trajno uklonjen sadržaj | Opcionalno, ali korisno | U praksi brže deindeksiranje nego 404 |
App Router 404 obrada#
Koristite notFound() za zapise koji nedostaju.
// 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.
// 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.5sINP <= 200msCLS <= 0.1
Za detaljniji performance playbook, pogledajte Optimizacija performansi web stranice.
Što provjeriti specifično u Next.js-u#
| Metrika | Najčešći Next.js uzroci | Popravci koji stvarno pomiču metrike |
|---|---|---|
| LCP | Neoptimizirana hero slika, spor TTFB, blokirajući fontovi | Next Image, caching, streaming SSR, optimizacija fontova |
| INP | Teška hidratacija, veliki client bundleovi, skupi event handleri | Prebacite logiku na server, smanjite JS, code split, koristite Server Components |
| CLS | Slike bez dimenzija, dinamički ubacivani banneri, font swapovi | Eksplicitne dimenzije, stabilan layout, font-display strategija |
LCP: popravite hero sliku i priority#
// 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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| Statično gdje god je moguće | Marketinške i evergreen stranice prerenderirane | Brže indeksiranje i bolji CWV |
| Revalidacija je namjerna | revalidate usklađen sa svježinom sadržaja | Izbjegava zastarjele SERP snippete |
| CDN caching nije “slomljen” | Cache headeri su dosljedni | Smanjuje opterećenje origina i ubrzava botove |
Primjer: revalidirajte sadržaj svaki sat#
// app/blog/[slug]/page.tsx
export const revalidate = 3600;Primjer: fetch caching usklađen s revalidacijom#
// 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 tip | Gdje pomaže | Minimalna polja za validaciju |
|---|---|---|
| Organization | Znanje o brendu i povjerenje | name, url, logo |
| WebSite + SearchAction | Eligibility za sitelinks search box | url, potentialAction |
| BreadcrumbList | Breadcrumb rich rezultati | itemListElement |
| Article | Article rich rezultati | headline, image, datePublished, author |
| Product | Product rich rezultati | name, 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.
// 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:
// 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ме#
// 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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| Postoje kategorijske i hub stranice | Jasni tematski hubovi s linkovima na ključne stranice | Bolji discovery i relevantnost |
| Linkovi su stvarni anchor linkovi | Ne samo click handleri | Crawlable navigacija |
| Nema orphan stranica | Svaki indeksabilan URL je interno linkan | Sprječ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#
| Provjera | Kriterij prolaza | Učinak |
|---|---|---|
| metadataBase koristi produkcijsku domenu | Nema staging URL-ova u kanonikalima ili OG URL-ovima | Sprječava pogrešnu kanonikalnu konsolidaciju |
| robots pravila se razlikuju kako treba | Staging je blokiran, produkcija je otvorena | Izbjegava indeksiranje staginga |
| Sitemapovi navode samo produkcijske URL-ove | Nema miješanih hostova | Sprječ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.tsisitemap.tsi 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
Više iz kategorije Web razvoj
Sve →Dinamične Open Graph slike u Next.js-u: generiranje OG slika, predmemoriranje, fontovi i savjeti za Edge runtime
Praktičan vodič za 2026. o dinamičnim Open Graph slikama u Next.js-u: generiranje OG slika po stranici, učitavanje fontova, cache headeri, Edge runtime zamke i rješavanje problema pri deployu.
Izgradnja React dizajn sustava s dizajnerskim tokenima: Tailwind CSS + Radix UI + TypeScript
Praktičan vodič za 2026. o izgradnji React dizajn sustava s Tailwindom i Radixom uz dizajnerske tokene, pristupačne primitive, tematiziranje i ponovno iskoristive pakete kroz više aplikacija.
Next.js + Supabase SaaS početna arhitektura (App Router): Auth, RLS, naplata i multi-tenancy
Production-ready nacrt za Next.js App Router + Supabase SaaS početnu arhitekturu: autentikacija, Postgres podatkovni model, RLS politike, Stripe naplata i multi-tenant dizajn organizacija s konkretnim primjerima.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Kontrolni popis za migraciju na Next.js App Router (s Pages Routera) + česte zamke
Praktičan, korak-po-korak plan migracije na Next.js App Router s Pages Routera, uključujući kontrolni popis za routing, dohvat podataka, SEO metadata, deployment i vodič za rješavanje čestih zamki.
Optimizacija performansi web stranice: Potpuni checklist (Next.js + Core Web Vitals) za 2026.
Praktičan, production-ready checklist za optimizaciju performansi web stranice u Next.js-u: Core Web Vitals, slike, lazy loading, CDN i caching—uz prije/poslije metrike i copy-paste konfiguraciju.
Dinamične Open Graph slike u Next.js-u: generiranje OG slika, predmemoriranje, fontovi i savjeti za Edge runtime
Praktičan vodič za 2026. o dinamičnim Open Graph slikama u Next.js-u: generiranje OG slika po stranici, učitavanje fontova, cache headeri, Edge runtime zamke i rješavanje problema pri deployu.