When building gaming wikis or database tools, developers face a brutal dichotomy: either rely on ad-bloated, slow-rendering legacy CMS platforms, or build a bespoke web app that risks getting buried under search engine penalties, slow edge response times, or crawler encoding errors.
Recently, I launched Grand Blue Roblox Wiki—a high-traffic companion app for an open-world pirate RPG. In this post, I want to break down the architectural lessons, real-world gotchas, and hard-learned engineering patterns that took our site to a perfect 100/100 Enterprise SEO audit, sub-100ms global response times, and zero crawler warnings.
Here is the blueprint.
1. The Stack: Zero-Server Next.js 16 Static Export
For static content-heavy sites with interactive client widgets (like countdown timers and probability planners), full SSG (output: 'export') hosted on a global CDN is unbeatable in cost, security, and raw throughput.
- Framework: Next.js 16.3 (Turbopack + App Router)
- Hosting / CDN: Cloudflare Pages
- Styling: Tailwind CSS v4 (Vanilla, utility-first)
- Data Layer: TypeScript modules + JSON configs validated at build time
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
images: { unoptimized: true },
};
export default nextConfig;
2. The Cloudflare Edge Cache Bottleneck: Fixing 3.6s Cold TTFB
During initial Ahrefs audits, we noticed a perplexing anomaly: while local page loads were lightning-fast, external crawler audits reported Time to First Byte (TTFB) spikes exceeding 3,600ms on specific subpages!
The Root Cause
Next.js SSG with trailingSlash: true outputs directories like out/codes/index.html. Cloudflare Pages serves these seamlessly, but if your public/_headers only specifies caching for root assets or specific extensions (e.g., /*.html), directory requests hit cold origin workers instead of Cloudflare's high-speed Tiered Cache.
The Fix
Declare a catch-all wildcard rule in public/_headers leveraging edge s-maxage:
# public/_headers
/*
Cache-Control: public, max-age=0, s-maxage=600, must-revalidate
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
With s-maxage=600, must-revalidate, Cloudflare's edge holds the static HTML in RAM globally, dropping TTFB from 3,671ms down to <80ms.
3. SEO Architecture: Solving "Double Brand Cannibalization"
Next.js App Router introduced title templating:
// src/app/layout.tsx
export const metadata: Metadata = {
title: {
default: 'Grand Blue Codes & Wiki: Complete Roblox Guide',
template: '%s | Grand Blue Roblox Wiki',
},
};
This is elegant—until you examine what subpages actually export.
The Anti-Pattern
Subpages often declare their own titles like this:
// BAD: src/app/codes/page.tsx
export const metadata = {
title: 'Grand Blue Codes (September 2026)',
};
When Next.js compiles this, the template concats them into:
Grand Blue Codes (September 2026) | Grand Blue Roblox Wiki
Notice the problem?
- Brand Cannibalization: "Grand Blue" is repeated twice, diluting ranking power.
-
SERP Pixel Truncation: The title balloons to 59–75 characters. Google's desktop cut-off is ~60 characters (and mobile is often tighter). The end of the title is brutally chopped off with
.... - In extreme cases (e.g.,
/wiki/page.tsx), you end up with:Grand Blue Wiki | Grand Blue Wiki!
The Dual-Track Standard
We enforced a strict separation of concerns:
- Subpages pass pure topic prefixes (18–28 characters), leaving the brand suffix to the root template:
// src/app/codes/page.tsx
export const metadata = generateSEOMetadata({
title: 'Active Codes & Free Rerolls', // 27 chars -> Renders to 52 chars
path: '/codes/',
});
-
Root Homepage (
src/app/page.tsx) passes a self-contained title (40–52 characters), because Next.js root layout templates do not apply to the root page:
export const metadata = generateSEOMetadata({
title: 'Grand Blue Codes & Wiki: Complete Roblox Guide', // 46 chars
path: '/',
});
Every single rendered title across our 24 pages now lands strictly between 46 and 56 characters.
4. Platform Entity Disambiguation: Defeating the Pop-Culture Collision
Here is a common problem in gaming SEO: our game is called Grand Blue.
Guess what else is named Grand Blue? One of the most famous comedy anime/manga series in the world (Grand Blue Dreaming), alongside Granblue Fantasy.
When users search for the anime, Google serves anime wikis, Crunchyroll, and MyAnimeList. But when players search for our game, they search for grand blue roblox codes, grand blue roblox wiki, or grand blue roblox tier list.
If your site omits the platform keyword, Google's Knowledge Graph gets confused. You end up ranking on page 8 for an anime query you never intended to compete for.
The Solution: Media-Grade H1 Standard
We adopted the convention used by tier-1 gaming outlets like IGN, Game8, and Fandom:
-
Site Brand:
Grand Blue Roblox Wiki(platform explicitly declared in the root title template). -
H1 Headings:
[Game] (Platform) [Topic]- ✅
Grand Blue (Roblox) Codes - ✅
Grand Blue (Roblox) Tier List - ✅
Grand Blue (Roblox) Cursed Fruits - ❌
Grand Blue Codes(missing platform entity) - ❌
Grand Blue Roblox Codes(sounds robotic / spammy)
- ✅
This creates an airtight semantic relationship: human readers get clean, natural phrasing, while search crawlers immediately tie the entity to the Roblox gaming ecosystem.
5. The "Corrupted Characters" Bug: Why You Must Ban Em-Dashes in Meta Tags
Did you know an innocent em-dash (—) can hurt your SEO reports?
In markdown and copywriting, dashes are great:
Grand Blue controls — standard scheme — verify in-game.
However, across Windows build environments, Node file streams, and international crawlers (like Ahrefs or Bingbot), UTF-8 / Windows-1252 mismatch can cause \u2014 to be misread as \x14 or an unprintable replacement block. Ahrefs immediately throws:
⚠️ Corrupted characters in title
⚠️ Corrupted characters in description
The Rule: Pure ASCII for Metadata
For all <title>, <meta name="description">, OpenGraph, and Twitter tags, enforce 100% pure half-width ASCII:
- Hyphens (
-), colons (:), or commas (,) only. - Strict ban on
—,–, arrows→, and curly quotes“ ” ‘ ’.
We automated this in our generateSEOMetadata utility:
const cleanDesc = description
.replace(/—/g, '-')
.replace(/–/g, '-')
.replace(/→/g, 'to')
.replace(/[“”]/g, '"')
.replace(/[‘’]/g, "'");
6. Prebuild Automated Discovery & Validation Pipeline
Manual audits fail as soon as you have more than 10 pages. We integrated prebuild and postbuild gates right into package.json:
"scripts": {
"prebuild": "node scripts/optimize-media.mjs && node scripts/gen-content-dates.mjs",
"build": "next build",
"postbuild": "node scripts/audit-links.mjs && node scripts/audit-orphans.mjs && node scripts/audit-author-eeat.mjs"
}
What these gates protect:
-
Zero-404 Media Prebuild: Recursively scans all
.tsxfiles for video IDs and images, downloading and converting missing assets to local 480x270 WebP thumbnails before Next.js compiles. -
Zero Orphan Page Audit: Traverses BFS link routes starting from
/to confirm every single page insitemap.xmlhas at least 2 incoming internal links. -
E-E-A-T Integrity Guard: Validates Schema.org
Person/Organizationmarkup, ensuring transparency without fake "Author" claims.
When npm run build exits with code 0, we have mathematically verified that the static artifact contains zero broken links, zero oversized titles, and zero missing schemas.
7. Results & Takeaways
By treating technical SEO and edge performance as foundational engineering requirements rather than post-launch marketing tweaks, our production build outputs:
- 🚀 24 / 24 Pages scoring 100/100 on technical SEO audit
- ⚡ Zero Layout Shifts (CLS = 0.00) through strict header min-height locking
- 🛡️ Sub-100ms global edge TTFB on Cloudflare Pages
- 📈 100% platform entity alignment across search consoles
If you are building your next niche wiki, directory, or documentation portal with Next.js, don't leave SEO to chance. Treat your title budgets, ASCII character sets, and edge headers with the same rigor you treat your database schemas!
Check out the live site at robloxgrandblue.wiki. Feel free to drop questions or share your own Next.js SSG gotchas in the comments below!
Top comments (0)