Skip to content

SEO và ảnh chia sẻ mạng xã hội ​

SRC-1211 (chủ dự án, 03.10.2026): link một trang chương trình dán lên Facebook hay Zalo phải hiện đúng ảnh, tiêu đề và mô tả của chương trình đó, ở cả nemo12.com lẫn learn.nemo12.com; và mọi trang nemo12.com phải qua một lượt rà SEO, kèm một cổng tự động để lỗi không quay lại.

Ảnh chia sẻ (og:image) ​

PhầnỞ đâuViệc
Bảng thẻapps/web/src/site/shareCards.tsChương trình nào có thẻ (mọi chương trình trong programs.ts không soon), đường dẫn nào thuộc chương trình nào (đoạn đầu, sau /parents), tên file.
Bộ vẽapps/web/scripts/og-images.mjsLúc build: ảnh hero của trang gốc chương trình (public/art/pages/<gốc>.svg, xem ảnh minh họa) + tên chương trình + câu hứa (PROGRAMS[id].line), vẽ ra PNG 1200x630 bằng @resvg/resvg-wasm với font Be Vietnam Pro. Ra public/og/*.png (gitignore, sinh lại mỗi build), mỗi ảnh khoảng 80 tới 130 KB, quá 300 KB là build đỏ.
Gắn vào trangapps/web/scripts/prerender.mjs (withShareCard)Mọi trang dưới gốc một chương trình (cả hai vai, mọi tab, trang nội dung) mang thẻ của chương trình: og:image, og:image:alt, og:image:type, twitter:image. Trang khác giữ thẻ chung /og-image.png.

PNG chứ không SVG: Facebook và Zalo không hiện og:image dạng SVG. Tên file là URL công khai ổn định (learn và các link đã chia sẻ trỏ vào), nên không đổi theo đường dẫn: NEMO WALK luôn là walk.png dù trang www đang ở /nemo-walk hay /walk.

Thẻ hiện có: ielts, sat, ap, grammar, essay, speak, walk, math, ai-teen, cùng hai bản tiếng Anh ielts-en và walk-en cho learn (luật ngôn ngữ trong CLAUDE.md: learn /ielts tiếng Anh, learn /walk tiếng Anh rất đơn giản).

learn.nemo12.com ​

learn là SPA phục vụ bằng static assets với not_found_handling: "single-page-application", nên mọi URL trả cùng một index.html; bot xem trước link không chạy JavaScript nên thấy cùng một tiêu đề cho mọi chương trình. apps/learn/scripts/share-meta.mjs chạy sau vite build và ghi một vỏ HTML cho mỗi trang vào của chương trình (dist/ielts.html, dist/sat.html...): đúng index.html đã build, chỉ khác <html lang> và các thẻ trong <head>. Cloudflare phục vụ ielts.html tại /ielts (html_handling mặc định); đường sâu hơn (/ielts/reading/...) vẫn rơi về index.html, nên SPA không đổi gì. Hệ quả nhỏ: /ielts/ (có gạch chéo cuối) nay 307 về /ielts.

Mười một trang vào: /ielts, /walk (en, og:locale en_US), /sat, /ap, /grammar, /essay, /speak, /math, /ai-teen, /programs, /books (vi, vi_VN). Ảnh trỏ sang https://nemo12.com/og/*.png. public/robots.txt của learn vẫn chặn máy tìm kiếm, nhưng cho các bot xem trước link (facebookexternalhit, Facebot, Zalo, Twitterbot, LinkedInBot...) đọc đúng mười một đường dẫn ấy; test src/shareMeta.test.ts giữ hai danh sách khớp nhau.

Facebook giữ ảnh cũ ​

Facebook (và Zalo) cache bản xem trước theo URL. Link đã từng được chia sẻ trước bản deploy này vẫn hiện ảnh cũ cho tới khi được quét lại: mở Facebook Sharing Debugger (developers.facebook.com/tools/debug), dán URL, bấm "Scrape Again". Việc này chủ dự án làm tay, mỗi URL quan trọng một lần.

Cổng SEO ​

apps/web/scripts/seo-gate.mjs, chạy cuối npm run build (sau prerender), đọc đúng HTML trong dist/ mà bot nhận được. Build đỏ khi:

  • trang được lập chỉ mục thiếu <title>, meta description, canonical, hay một trong các thẻ og:title/description/url/image/image:width/image:height/image:alt/type/locale/site_name, twitter:card;
  • hai trang tự canonical trùng tiêu đề hoặc trùng mô tả;
  • canonical trỏ vào một bí danh 301, vào trang không tồn tại, hoặc vào trang lại canonical tiếp đi chỗ khác; og:url khác canonical;
  • og:image không phải URL https tuyệt đối, là SVG, file không có trong dist/, hoặc trang chương trình không mang thẻ của chương trình mình;
  • sitemap có URL chuyển hướng, URL không có trang, URL noindex, hoặc URL không phải canonical của chính nó;
  • link nội bộ <a href> dẫn tới 404 (link qua một 301 thì đếm và in ra, không chặn);
  • JSON-LD không parse được, breadcrumb trong JSON-LD trỏ vào bí danh 301, hoặc trang có số h1 khác 1;
  • robots.txt thiếu dòng Sitemap hoặc chặn cả site.

Độ dài (tiêu đề trên 65 ký tự, mô tả ngoài khoảng 70 tới 170) chỉ là cảnh báo: vài tên chương trình dài có chủ đích. npm run seo:gate -- --report in toàn bộ.

Trang bài viết dựng từ D1 (worker/articles) nằm ngoài dist/, nên cùng luật head được giữ bằng test worker/articles/handler.test.ts cho ba loại trang: hub, category, bài.

Kết quả lượt rà 03.10.2026 ​

Đo trên bản build của origin/main trước khi sửa: 639 trang, 577 trang tự canonical, 577 URL sitemap. Phần lớn nền đã đúng từ các lượt trước (SRC-694, SRC-1150, SRC-1190): canonical, sitemap chỉ chứa bản chính, một h1, JSON-LD.

LỗiTrướcSau
Link nội bộ gãy (/speak/resources trỏ /speak/nemo-walk)10
Link nội bộ đi qua 301 (/start trỏ /courses/ielts-writing)10
Trang gốc chương trình có tiêu đề của MỤC TIÊU ("IELTS · Luyện IELTS, SAT, Grammar")100
Trang chương trình dùng ảnh chia sẻ chung thay vì ảnh của chương trìnhmọi trang0
Trang gốc chương trình không khai Course trong JSON-LD140
Cảnh báo độ dài tiêu đề/mô tả (chủ yếu mô tả sinh tự động quá ngắn)837
Trang learn có thẻ chia sẻ riêng0/1111/11

Bảy cảnh báo còn lại là chữ viết tay, để chủ dự án quyết nếu muốn: mô tả /ap/courses (204 ký tự), /speak/curriculum (171), /speak/resources (182), /teachers/tefl/compare (181), dòng /parents/ap (63, dùng chung với thẻ trên trang mục tiêu), và /login (noindex, không quan trọng).