
Headless CMS Magyarországon: Sanity vs Strapi vs Contentful 2026
Három headless CMS deep-dive: Sanity, Strapi, Contentful. Pricing, real-time preview, lokalizáció, és 5 magyar piaci use case ajánlás.
25 konkrét pont, ami Next.js 16 alapú projekt go-live előtt nem maradhat ki. Security, performance, SEO, accessibility és monitoring kategóriákban.

Pre-launch checklist
CSP, HSTS, X-Frame, Referrer-Policy. Rate-limit kritikus endpoint-okra, .env soha git-ben.
SSG/ISR ahol lehet, next/image + sizes prop, bundle-analyzer regression-check. Lighthouse 90+ mobil.
Metadata + JSON-LD + sitemap + robots. hreflang multi-locale. WCAG AA contrast + keyboard nav.
Sentry source-map upload, Vercel Analytics / Plausible, uptime-monitor Slack-alert-tel.
A Next.js 16 stabil verzióban van 2026 elején, Turbopack default-tal és új App Router-feature-ekkel. Egy production-ready deploy-hoz viszont sok apró pont van, amit könnyű kifelejteni. Ez a checklist 25 konkrét item, kategorizálva. A pontok 2026 Q1-es Next.js 16.2+ állapotot tükröznek; az új release-ek hozhatnak változást, érdemes a hivatalos doc-okat is utánaolvasni.
A 25 pont nem teljes — projekt-szinten több is jöhet (GDPR, analytics-szegmentáció, A/B teszt-infrastruktúra). De ezzel a checklist-tel a launch napon nem lesz „de hát ezt is kellett volna" pánik.
Mielőtt belekezdünk a checklist-be, néhány Next.js 16-os changes, ami a production-deploymentet befolyásolja:
cookies(), headers(), params, searchParams mind async-szá váltak. Existing kódbázis upgrade-jénél kötelező átírás.use() hook, useFormStatus, useActionState.'use cache' directive a granular caching-hoz.Figyelem: A breaking change-eket az upgrade során a
npx @next/codemod upgradeparancs nagyrészt automatikusan kezeli. De manuális ellenőrzés is kell — különösen a custom middleware-eken és a useSearchParams-hookokon.
CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy. Egy minimal beállítás:
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://va.vercel-scripts.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
].join('; '),
},
],
},
];
},
};
export default nextConfig;
CSP nonce-megoldás dinamikus inline-script-ekre: next/script használata, vagy a middleware-ben generált nonce.
.gitignore-ban .env* mintával fedve. Csak .env.example a repóban template-ként.
# .gitignore
.env
.env.local
.env.*.local
.env.production
# .env.example
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://user:pass@host:5432/db
OPENAI_API_KEY=sk-...
A secret-leak-protection second layer: git-secrets vagy gitleaks a CI-ben.
Next.js 15+ built-in Origin header check. Ne kapcsold ki manuálisan. A Server Action automatikusan validálja, hogy a request a saját domain-jéről jött.
// app/actions/contact.ts
'use server';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
message: z.string().min(10).max(2000),
});
export async function submitContact(formData: FormData) {
const parsed = schema.safeParse({
email: formData.get('email'),
message: formData.get('message'),
});
if (!parsed.success) {
return { error: 'Invalid input', issues: parsed.error.issues };
}
// Server-side action — CSRF-protected by default
await db.contacts.insert(parsed.data);
return { success: true };
}
Vercel KV / Upstash Redis + @upstash/ratelimit. Form submission, login, API call.
// middleware.ts
import { NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '60 s'),
analytics: true,
});
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/api/')) {
const ip = request.ip ?? '127.0.0.1';
const { success, limit, remaining } = await ratelimit.limit(ip);
if (!success) {
return new NextResponse('Too Many Requests', {
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
},
});
}
}
return NextResponse.next();
}
</ karakterekreA JSON-LD script-bem injectolva XSS-veszélyt jelenthet:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(schema).replace(/</g, '\\u003c'),
}}
/>
A </ karaktert escape-elni kell, mert pl. egy </script> substring breaking out a script-tag-ből.
npm audit clean (0 vulnerabilities). Ha Next.js függőség miatt nem fix-elhető, npm overrides-szal force-old.
{
"overrides": {
"postcss": "^8.4.31"
}
}
Plus a CI-ben:
# .github/workflows/security.yml
- name: Dependency audit
run: npm audit --audit-level=high
- name: License check
run: npx license-checker --failOn 'GPL-3.0'
generateStaticParams minden dinamikus route-ra. ISR (revalidate) ha a tartalom változik, de nem real-time. RSC default, client component csak ahol kell.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
export const revalidate = 3600; // 1 hour ISR
export default async function BlogPost({ params }) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return <Article post={post} />;
}
Minden raster image-re. placeholder="blur" és sizes prop megadva.
import Image from 'next/image';
<Image
src="/blog/images/post-slug/hero.jpg"
alt="Descriptive alt text"
width={1200}
height={630}
sizes="(max-width: 768px) 100vw, 1200px"
priority={isAboveFold}
placeholder="blur"
blurDataURL="data:image/svg+xml;base64,..."
/>
A priority prop az above-the-fold képekre — kikényszeríti az eager loading-ot.
import { Inter, Geist_Mono } from 'next/font/google';
const inter = Inter({
subsets: ['latin', 'latin-ext'], // magyar karakterek
display: 'swap',
variable: '--font-sans',
});
const geistMono = Geist_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-mono',
});
A subsets-szel csak a szükséges karakterek töltődnek. Magyar nyelvre latin-ext kötelező (ű, ő, etc.). Self-hosted opció kapcsolódik a build-time-hoz.
@next/bundle-analyzer plugin. CI-ban regression check: ha a bundle 20%-kal nő, alert.
// next.config.ts
import bundleAnalyzer from '@next/bundle-analyzer';
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
export default withBundleAnalyzer({...});
ANALYZE=true npm run build
# Megnyitja a böngészőben a bundle-tree-t
CI-integration példa size-limit-tel:
{
"size-limit": [
{
"path": ".next/static/chunks/*.js",
"limit": "180 KB"
}
]
}
App Router default-ban split-el. Kerüld a 'use client' direktívát root-szinten — komponensekre szűken alkalmazd.
// page.tsx — Server Component (default)
import ClientCounter from './client-counter';
export default async function Page() {
const data = await fetchServerData();
return (
<div>
<h1>{data.title}</h1>
{/* Csak ez a komponens lesz client-side */}
<ClientCounter initial={data.count} />
</div>
);
}
// client-counter.tsx — Client Component
'use client';
import { useState } from 'react';
export default function ClientCounter({ initial }) {
const [count, setCount] = useState(initial);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
loading="lazy" default a next/image-en. Above-the-fold image-eknél priority explicit. A többi alatti kép viewportra-érkezésig nem töltődik.
Cache-Control: public, max-age=31536000, immutable)cache: 'force-cache' vagy 'no-store'// Server Component
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }, // 1 hour ISR
});
return res.json();
}
A Next.js 16-ban a 'use cache' directive még granular-abb kontrollt ad:
async function getProductList() {
'use cache';
cacheLife('hours');
cacheTag('products');
return db.products.findAll();
}
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Page title — Brand',
description: 'Page description ~150 char',
openGraph: {
title: 'OG title',
description: 'OG description',
images: ['/og-image.jpg'],
locale: 'hu_HU',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Twitter title',
description: 'Twitter description',
images: ['/twitter-image.jpg'],
},
alternates: {
canonical: 'https://example.com/page',
languages: {
'hu-HU': 'https://example.com/hu/page',
'en-US': 'https://example.com/en/page',
},
},
};
Dynamic metadata:
export async function generateMetadata({ params }): Promise<Metadata> {
const { slug } = await params;
const post = await getPostBySlug(slug);
return {
title: post.title,
description: post.excerpt,
openGraph: { images: [post.image] },
};
}
src/app/sitemap.ts Next.js native support. Tartalmazza minden statikus és dynamic route-ot.
// app/sitemap.ts
import type { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://example.com';
const posts = await getAllPosts();
const blogUrls = posts.map((p) => ({
url: `${baseUrl}/blog/${p.slug}`,
lastModified: p.updatedAt,
changeFrequency: 'weekly' as const,
priority: 0.7,
}));
return [
{ url: baseUrl, priority: 1.0 },
{ url: `${baseUrl}/blog`, priority: 0.8 },
...blogUrls,
];
}
src/app/robots.ts formában. Dev/staging env-en Disallow: / minden user-agentre.
// app/robots.ts
import type { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
const isProduction = process.env.NEXT_PUBLIC_ENV === 'production';
if (!isProduction) {
return {
rules: { userAgent: '*', disallow: '/' },
};
}
return {
rules: [
{ userAgent: '*', allow: '/', disallow: ['/api/', '/admin/'] },
],
sitemap: 'https://example.com/sitemap.xml',
};
}
Organization, BlogPosting, FAQPage, Product schema relevánsan. Validate Schema Markup Validator-rel.
const schema = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
datePublished: post.date,
author: {
'@type': 'Organization',
name: 'Corevanix',
},
image: post.coverImage,
};
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(schema).replace(/</g, '\\u003c'),
}}
/>
alternates.languages a metadata-ban minden multi-locale page-en. Tested with Google Search Console.
Minden interactive element keyboardról elérhető. Focus-visible ring látszik (focus-visible:ring-2).
<button
className="rounded bg-accent px-4 py-2 focus-visible:outline-none
focus-visible:ring-2 focus-visible:ring-accent-primary
focus-visible:ring-offset-2"
>
Click me
</button>
aria-label minden ikon-button-on, aria-labelledby szekciókban, aria-expanded accordion-okon.
<button aria-label="Bezárás" onClick={onClose}>
<X aria-hidden="true" />
</button>
<nav aria-label="Főnavigáció">
<ul>...</ul>
</nav>
<button aria-expanded={isOpen} aria-controls="menu">
Menu
</button>
Text vs background min. 4.5:1, large text 3:1. Lighthouse audit teszteli, plus manual check Tools like WebAIM Contrast Checker.
A Tailwind dark/light mode-on mindkét variant ellenőrizendő.
<a
href="#main"
className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4
focus:bg-accent-primary focus:px-4 focus:py-2 focus:rounded"
>
Skip to main content
</a>
<main id="main">...</main>
@sentry/nextjs setup. Source map upload Vercel build-on. Release-tracking commit SHA-val.
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
environment: process.env.NEXT_PUBLIC_ENV,
release: process.env.NEXT_PUBLIC_COMMIT_SHA,
});
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.05,
replaysOnErrorSampleRate: 1.0,
integrations: [Sentry.replayIntegration()],
});
Vercel Analytics (free tier), Plausible vagy PostHog. GDPR-compliant és cookieless preferred.
import { Analytics } from '@vercel/analytics/next';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
UptimeRobot (ingyenes), Better Stack vagy Vercel built-in. Slack / email alert downtime-on.
Recommended setup:
Mielőtt push-olod a main-be:
npm run lint # 0 error
npm run typecheck # 0 error
npm run build # success, no warning
npm run start # smoke test localhost
npx unlighthouse # Lighthouse 90+ minden kategóriában
| Kategória | Cél | Ideális |
|---|---|---|
| Performance | 90+ | 95+ |
| Accessibility | 95+ | 100 |
| Best Practices | 95+ | 100 |
| SEO | 95+ | 100 |
Mobil eszközön (mid-range) mérve. A laptopon majdnem mindenki 100-at ér el — a mobil a release-critical.
| Metrika | Good | Needs improvement | Poor |
|---|---|---|---|
| LCP | < 2.5s | 2.5-4.0s | > 4.0s |
| CLS | < 0.1 | 0.1-0.25 | > 0.25 |
| INP | < 200ms | 200-500ms | > 500ms |
// app/error.tsx
'use client';
import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';
export default function Error({ error, reset }) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);
return (
<div>
<h2>Valami hiba történt</h2>
<button onClick={() => reset()}>Újrapróbálás</button>
</div>
);
}
// app/not-found.tsx
import Link from 'next/link';
export default function NotFound() {
return (
<div>
<h2>Az oldal nem található</h2>
<Link href="/">Vissza a főoldalra</Link>
</div>
);
}
// app/blog/[slug]/loading.tsx
export default function Loading() {
return <BlogPostSkeleton />;
}
// app/layout.tsx
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
</head>
{
"framework": "nextjs",
"buildCommand": "npm run build",
"regions": ["fra1"],
"github": {
"silent": false
}
}
fra1 (Frankfurt) régió EU-data-ra. Multi-region csak ha nagy globális forgalom.
Témához kapcsolódó saját cikkeink: Headless CMS Magyarországon — content-management Next.js mellé. Webshop konverziós ráta javítása — e-commerce performance. LLM hallucinációk elleni 7 védekezés — ha AI-feature-t építesz Next-be.
A 25 pont nem teljes — projekt-szinten több is jöhet. De ezzel a checklist-tel a launch napon nem lesz „de hát ezt is kellett volna" pánik.
A pre-launch audit 1-2 napos folyamat: végigmenni a 25+5 ponton, mérni, fix-elni a gap-eket. Ezt a hyper-care első hetébe nem szabad rakni — a launch előtti utolsó hét sprint-jébe való.
Ha Next.js projektet tervezel production-be, beszéljük át a deployment-stratégiát — a 25 pont mindegyikére van bevett megoldásunk. Az első release-en a partner-developerünk személyesen kíséri végig a checklist-en, hogy az átadás-átvétel tisztán menjen.
A szerzőről
Corevanix Kft.
Tech partner
Modern tech partner — SAP/ERP, webfejlesztés, AI automatizáció és mobil app fejlesztés egy szakmai csapatban. KKV-tól enterprise projektig.
LinkedIn →
Három headless CMS deep-dive: Sanity, Strapi, Contentful. Pricing, real-time preview, lokalizáció, és 5 magyar piaci use case ajánlás.

Tíz konkrét technikai optimalizáció, ami mérhetően növeli a webshop konverziót. Performance, UX, trust signal, checkout-flow és A/B tesztelhető elemek.