Use Cases (Diperbarui: 3/6/2026)

Membuat peta situs XML dengan Claude Code

Buat peta situs Astro dan Node dengan hreflang, lastmod, robots.txt, serta pemeriksaan Search Console.

Membuat peta situs XML dengan Claude Code

Peta situs adalah daftar URL publik, bukan jaminan indeks

Saat Claude Code membantu menerbitkan banyak artikel, dokumentasi, atau halaman produk, masalahnya tidak selalu ada pada templat halaman. Sering kali masalahnya adalah apakah mesin pencari dapat menemukan URL yang benar. Peta situs XML memberi tahu URL kanonis yang ingin dirayapi, kapan halaman berubah secara penting, dan bagaimana versi terjemahan saling berhubungan.

Informasi ini harus akurat. Dokumentasi Google saat ini menyatakan bahwa priority dan changefreq diabaikan, sedangkan lastmod hanya berguna jika konsisten dengan perubahan nyata. Endpoint ping lama untuk peta situs juga sudah dihentikan. Jadi alur modern sebaiknya memakai robots.txt, Google Search Console, dan pemeriksaan setelah deploy, bukan https://www.google.com/ping?sitemap=....

Panduan ini membahas dua cara praktis: integrasi resmi Astro dan generator Node.js tanpa dependensi untuk koleksi MDX multibahasa. Untuk konteks SEO yang lebih luas, lihat juga optimasi SEO dengan Claude Code dan pengaturan CI/CD dengan Claude Code.

Aturan resmi yang perlu diikuti

HalKeputusan praktis
URLGunakan URL lengkap seperti https://example.com/blog/post/
BatasPisahkan sebelum 50.000 URL atau 50 MB tanpa kompresi
EncodingSimpan sebagai UTF-8 dan lakukan escape nilai XML
lastmodPakai tanggal perubahan penting yang benar-benar terjadi
priority / changefreqDapat dihilangkan untuk Google
Halaman multibahasaSetiap URL mencantumkan dirinya sendiri dan semua versi bahasa lain
PengirimanGunakan robots.txt dan Search Console; hapus skrip ping

Sumber utamanya adalah panduan peta situs Google, pengumuman penghentian ping Google, panduan versi lokal, dan protokol sitemaps.org.

Kasus 1: halaman Astro dan rute blog

Untuk situs Astro statis, mulai dari @astrojs/sitemap. Integrasi ini berjalan saat astro build dan dapat menambahkan hubungan bahasa jika struktur rutenya jelas.

npx astro add sitemap
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://claudecodelab.com',
  integrations: [
    sitemap({
      filter: (page) => !page.includes('/draft/') && !page.includes('/preview/'),
      i18n: {
        defaultLocale: 'ja',
        locales: {
          ja: 'ja',
          en: 'en',
          zh: 'zh-CN',
          ko: 'ko',
          es: 'es',
          fr: 'fr',
          de: 'de',
          pt: 'pt-BR',
          hi: 'hi',
          id: 'id',
        },
      },
    }),
  ],
});

Kesalahan yang sering terjadi adalah nilai site salah. Jangan biarkan localhost, domain pratinjau, atau campuran http dan https masuk ke peta situs publik. Google akan merayapi URL persis seperti yang tertulis, jadi URL tersebut harus sama dengan URL kanonis halaman.

Kasus 2: generator Node.js untuk MDX multibahasa

Generator khusus berguna saat konten berada di koleksi seperti blog, blog-en, dan blog-zh, atau saat updatedDate harus menjadi lastmod. Contoh berikut hanya memakai modul bawaan Node.js dan menulis public/sitemap.xml.

// scripts/generate-sitemap.mjs
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
import path from 'node:path';

const SITE_URL = (process.env.SITE_URL ?? 'https://example.com').replace(/\/$/, '');
const OUT_DIR = 'public';
const OUT_FILE = path.join(OUT_DIR, 'sitemap.xml');

const collections = [
  { dir: 'site/src/content/blog', prefix: '/blog', hreflang: 'ja' },
  { dir: 'site/src/content/blog-en', prefix: '/en/blog', hreflang: 'en' },
  { dir: 'site/src/content/blog-zh', prefix: '/zh/blog', hreflang: 'zh-CN' },
  { dir: 'site/src/content/blog-ko', prefix: '/ko/blog', hreflang: 'ko' },
  { dir: 'site/src/content/blog-es', prefix: '/es/blog', hreflang: 'es' },
  { dir: 'site/src/content/blog-fr', prefix: '/fr/blog', hreflang: 'fr' },
  { dir: 'site/src/content/blog-de', prefix: '/de/blog', hreflang: 'de' },
  { dir: 'site/src/content/blog-pt', prefix: '/pt/blog', hreflang: 'pt-BR' },
  { dir: 'site/src/content/blog-hi', prefix: '/hi/blog', hreflang: 'hi' },
  { dir: 'site/src/content/blog-id', prefix: '/id/blog', hreflang: 'id' },
];

function escapeXml(value) {
  return String(value).replace(/[<>&'"]/g, (char) => ({
    '<': '&lt;',
    '>': '&gt;',
    '&': '&amp;',
    "'": '&apos;',
    '"': '&quot;',
  })[char]);
}

async function* walk(dir) {
  let items;
  try {
    items = await readdir(dir, { withFileTypes: true });
  } catch (error) {
    if (error.code === 'ENOENT') return;
    throw error;
  }

  for (const item of items) {
    const fullPath = path.join(dir, item.name);
    if (item.isDirectory()) {
      yield* walk(fullPath);
    } else if (/\.(md|mdx)$/.test(item.name)) {
      yield fullPath;
    }
  }
}

function frontmatterOf(source) {
  return source.match(/^---\n([\s\S]*?)\n---/)?.[1] ?? '';
}

function dateField(frontmatter, key) {
  return frontmatter.match(new RegExp(`^${key}:\\s*["']?(\\d{4}-\\d{2}-\\d{2})`, 'm'))?.[1];
}

function routeSlug(collectionDir, filePath) {
  return path
    .relative(collectionDir, filePath)
    .replace(/\\/g, '/')
    .replace(/\.(md|mdx)$/, '')
    .replace(/\/index$/, '');
}

function encodeRoute(slug) {
  return slug.split('/').map(encodeURIComponent).join('/');
}

async function collectEntries() {
  const bySlug = new Map();

  for (const collection of collections) {
    for await (const filePath of walk(collection.dir)) {
      const source = await readFile(filePath, 'utf8');
      const frontmatter = frontmatterOf(source);
      if (/^draft:\s*true\s*$/m.test(frontmatter)) continue;

      const info = await stat(filePath);
      const slug = routeSlug(collection.dir, filePath);
      const lastmod =
        dateField(frontmatter, 'updatedDate') ??
        dateField(frontmatter, 'pubDate') ??
        info.mtime.toISOString().slice(0, 10);

      const route = `${collection.prefix}/${encodeRoute(slug)}/`;
      const variant = {
        loc: `${SITE_URL}${route}`,
        hreflang: collection.hreflang,
        lastmod,
      };

      const variants = bySlug.get(slug) ?? [];
      variants.push(variant);
      bySlug.set(slug, variants);
    }
  }

  return [...bySlug.values()].flatMap((variants) =>
    variants.map((variant) => ({
      ...variant,
      alternates: variants.map(({ hreflang, loc }) => ({ hreflang, loc })),
    })),
  );
}

function buildSitemap(entries) {
  const urls = entries.map((entry) => `  <url>
    <loc>${escapeXml(entry.loc)}</loc>
    <lastmod>${entry.lastmod}</lastmod>
${entry.alternates.map((alt) => `    <xhtml:link rel="alternate" hreflang="${escapeXml(alt.hreflang)}" href="${escapeXml(alt.loc)}" />`).join('\n')}
  </url>`).join('\n');

  return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
${urls}
</urlset>
`;
}

const entries = await collectEntries();
if (entries.length === 0) {
  throw new Error('Tidak ada URL publik untuk peta situs.');
}

await mkdir(OUT_DIR, { recursive: true });
await writeFile(OUT_FILE, buildSitemap(entries), 'utf8');
console.log(`${entries.length} URL ditulis ke ${OUT_FILE}.`);

Jalankan seperti ini:

SITE_URL=https://claudecodelab.com node scripts/generate-sitemap.mjs

Kasus 3: pisahkan artikel, produk, dan dokumentasi

Blog kecil cukup memakai satu sitemap.xml. Situs yang lebih besar sebaiknya memisahkan peta situs berdasarkan jenis konten. Ini menjaga ukuran tetap di bawah batas resmi dan memudahkan pemeriksaan di Search Console.

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://example.com/sitemap-pages.xml</loc>
    <lastmod>2026-06-03</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-blog.xml</loc>
    <lastmod>2026-06-03</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/sitemap-products.xml</loc>
    <lastmod>2026-06-03</lastmod>
  </sitemap>
</sitemapindex>

Minta Claude Code mencatat jumlah URL per file, membagi sebelum 50.000 URL, dan memastikan indeks hanya menunjuk peta situs dari situs yang sama.

robots.txt, Search Console, dan pemeriksaan

Tambahkan peta situs ke robots.txt:

User-agent: *
Allow: /

Sitemap: https://claudecodelab.com/sitemap.xml

Lalu kirim URL tersebut sekali di Google Search Console. Dalam proses deploy, pastikan URL publik mengembalikan HTTP 200 dan terlihat seperti XML peta situs.

// scripts/verify-sitemap.mjs
const sitemapUrl = process.env.SITEMAP_URL ?? 'https://example.com/sitemap.xml';
const response = await fetch(sitemapUrl);

if (!response.ok) {
  throw new Error(`Permintaan peta situs gagal: HTTP ${response.status}`);
}

const xml = await response.text();
if (!xml.includes('<urlset') && !xml.includes('<sitemapindex')) {
  throw new Error('Respons tidak tampak seperti XML peta situs.');
}

console.log(`${sitemapUrl} sudah diperiksa. Ukuran: ${xml.length} bytes`);

Kesalahan yang harus dicegah sebelum publikasi

Kesalahan pertama adalah membuat semua lastmod sama dengan tanggal build. Jika isi halaman tidak berubah, tanggalnya juga tidak perlu berubah.

Kesalahan kedua adalah memasukkan draf, halaman noindex, sumber pengalihan, atau URL duplikat. Peta situs hanya berisi URL kanonis yang ingin muncul di hasil pencarian.

Kesalahan ketiga adalah hubungan hreflang satu arah. Setiap versi bahasa harus menunjuk dirinya sendiri dan semua versi bahasa lain.

Kesalahan keempat adalah lupa escape XML. Tanda & dalam query string harus menjadi &amp;.

Monetisasi dan hasil praktik

Peta situs tidak langsung menghasilkan pendapatan, tetapi menjaga jalur penemuan untuk halaman yang bernilai: tutorial, perbandingan, materi gratis, dan halaman konsultasi. Setelah peta situs diperbaiki, periksa tautan internal dan CTA agar pembaca dapat bergerak alami ke pelatihan Claude Code atau sumber terkait.

Dalam alur ClaudeCodeLab milik Masa, perubahan paling terasa adalah menghapus ping lama, menyamakan lastmod dengan updatedDate, dan menghubungkan sepuluh versi bahasa dengan hreflang timbal balik. Peninjauan editorial menjadi lebih jelas karena peta situs memantulkan tanggal dan slug yang sama dengan frontmatter MDX.

#Claude Code #peta situs #SEO #XML #otomatisasi
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.