Astro SEO — hướng dẫn kỹ thuật đầy đủ

Astro prerender HTML static theo mặc định và chỉ hydrate island cần tương tác, tạo nền tảng tốt cho SEO. Hướng dẫn này bao quát output mode, sitemap, metadata, Content Collections, hình ảnh, Server Islands, View Transitions và kiểm tra production.

Xuất bản lần đầu: 26 thg 6, 2026 · Cập nhật lần cuối: 22 thg 8, 2026 · Advanced
Ngôn ngữ

Astro phù hợp với SEO vì mặc định dựng sẵn từng tuyến thành HTML tĩnh và chỉ kích hoạt phía client cho thành phần được đánh dấu. Tuy nhiên, đây không phải bảo đảm cho mọi tuyến hoặc kết quả SEO: render phía server, thiết lập dựng sẵn theo tuyến, Server Islands và client:only đều thay đổi điều crawler nhận. Astro không tự tạo meta tag, canonical, sitemap, dữ liệu có cấu trúc hay robots.txt. Hãy cấu hình các phần đó, dùng Content Collections cùng Zod, xử lý hình LCP đúng, thêm URL chỉ tồn tại lúc chạy vào sitemap và kiểm tra phản hồi production thực tế.

Tóm tắt — Kiến trúc Astro phù hợp với SEO: theo mặc định, trang và endpoint được prerender thành HTML static nên nội dung không phải chờ hàng đợi render. Nhưng đây là mặc định, không phải bảo đảm cho mọi route; HTML static hay server không tự chứng minh khả năng crawl, lập chỉ mục, thứ hạng hoặc Core Web Vitals của URL production. Islands architecture chỉ hydrate component có directive client:*; phần còn lại gửi HTML mà không có JavaScript hydration riêng, dù script cấp trang, island khác và router vẫn có thể thêm JavaScript. Astro không tự tạo meta tag, canonical, sitemap hay dữ liệu có cấu trúc; hãy cấu hình rõ ràng, lý tưởng là xác thực bằng Content Collections và Zod. Server Islands cần adapter, gửi static shell có nội dung fallback trong document đầu tiên rồi tải nội dung deferred bằng request riêng; cần kiểm tra crawler thực sự nhận gì. View Transitions dùng history.pushState và an toàn cho SEO vì Google vẫn crawl các trang MPA nền. Tôi chạy patrickstox.com trên Astro và đã xác thực các tính năng dưới đây trên website triển khai thực tế, không chỉ trong môi trường local.

Evidence for this claim Astro prerenders pages as static HTML by default and only sends client JavaScript for explicitly hydrated components. Scope: Astro default static output and islands architecture. Confidence: high · Verified: Astro: Why Astro

Vì sao Astro tránh được vấn đề render JavaScript

Lý do JavaScript SEO khó là “làn sóng thứ hai”. Google tải HTML thô trước rồi đưa trang vào hàng đợi render bằng Chromium headless; chính hàng đợi này tạo rủi ro. Tài liệu Google mô tả: “Googlebot queues all pages with a 200 HTTP status code for rendering unless a robots meta tag tells Google not to index the page. The page may stay on this queue for a few seconds, but it can take longer than that.” (bản dịch) “Googlebot đưa mọi trang trả mã HTTP 200 vào hàng đợi render, trừ khi robots meta tag yêu cầu không lập chỉ mục. Trang có thể ở đó vài giây nhưng cũng có thể lâu hơn.” Với SPA render phía client, nội dung chưa tồn tại cho tới khi làn sóng render chạy.

Chế độ output mặc định của Astro là static: trang và endpoint được prerender thành tệp HTML hoàn chỉnh tại thời điểm build. Vì vậy, với route dùng mặc định này, HTML thô chính là trang đã render. Evidence for this claim Astro uses static output and prerenders routes at build time by default. Scope: Astro default output mode; routes can opt out of prerendering. Confidence: high · Verified: Astro: On-demand rendering Không có làn sóng thứ hai phải chờ vì không còn gì cần thực thi; Googlebot thấy toàn bộ nội dung ngay lần fetch đầu tiên. Joost de Valk, nhà sáng lập Yoast, nói: “From an SEO perspective, static HTML on a CDN is a better starting point than most CMSes will ever give you.” (bản dịch) “Xét từ góc độ SEO, HTML static trên CDN là điểm khởi đầu tốt hơn hầu hết CMS có thể cung cấp.”

Tuy nhiên, đó chỉ là mặc định chứ không phải thuộc tính phổ quát của mọi route. Đặt output: 'server' sẽ chuyển mặc định sang render theo yêu cầu; ngay cả dự án mặc định static cũng có thể cho một route opt out bằng adapter và export const prerender = false. Kiến trúc không tự bảo đảm kết quả: HTML static hoặc server, islands và adapters không tự bảo đảm khả năng crawl, lập chỉ mục, thứ hạng hay Core Web Vitals. Các kết quả đó phụ thuộc route đã triển khai và crawler cụ thể, nên phải kiểm tra thay vì giả định framework xử lý hết.

Điều này cũng hữu ích cho crawler hoàn toàn không render được. Google nói rõ “not all bots can run JavaScript” (bản dịch) “không phải bot nào cũng chạy được JavaScript” — đó là thực tế năm 2026 đối với phần lớn crawler AI và nhiều công cụ bên thứ ba. Ở những route thực sự được prerender, output HTML-first của Astro có thể được mọi crawler đọc, không chỉ Googlebot. Đây cũng là luận điểm trong SEO cho headless CMS: chế độ render là sản phẩm.

Islands architecture: JavaScript chỉ ở nơi bạn yêu cầu

Astro render component thành HTML và theo cách diễn đạt của tài liệu, gửi “just HTML & CSS, stripping out all client-side JavaScript automatically.” (bản dịch) “chỉ HTML và CSS, tự động loại bỏ toàn bộ JavaScript phía client.” Khả năng tương tác là opt-in. Bạn đánh dấu component bằng directive client:*client:load, client:idle hoặc client:visible — và chỉ island đó hydrate bằng JavaScript. Mọi phần khác giữ nguyên HTML static. Evidence for this claim Astro client directives selectively hydrate interactive islands while other components remain static HTML. Scope: Astro islands and client directives. Confidence: high · Verified: Astro: Islands

Đối với SEO, cách này gần lý tưởng. Nội dung Googlebot cần lập chỉ mục là HTML thuần, còn widget tương tác không kéo chậm phần còn lại. client:visible đặc biệt hữu ích: component dưới màn hình đầu tiên chỉ bắt đầu hydrate khi được cuộn vào vùng nhìn thấy, nên không chặn LCP. Khái niệm này bắt nguồn từ Jason Miller, người tạo Preact, khi mô tả selective hydration là “rendering HTML pages on the server, and inject[ing] placeholders or slots around highly dynamic regions” (bản dịch) “render trang HTML trên server và chèn placeholder hoặc slot quanh những vùng có tính động cao”.

Có hai điểm tinh tế cần phân biệt vì các bài so sánh thường nhập nhằng:

  • client:only là trường hợp khác. Không giống client:load/client:idle/client:visible, component client:only bỏ qua hoàn toàn render server và không tạo HTML trên server. Nội dung cần lập chỉ mục nằm chỉ trong component đó không có trong document Googlebot fetch; nó chỉ xuất hiện sau khi trình duyệt hydrate. Đừng đặt nội dung chính ở đó.
  • Đây là selective hydration, không phải resumability. Astro chạy lại code phía client của mỗi island từ đầu trong trình duyệt; nó không tiếp tục trạng thái thực thi được serialize trên server như mô hình resumability của Qwik. Hai cơ chế này không giống nhau.
Evidence for this claim A client:only component skips server rendering, so indexable content placed only inside it cannot be assumed to exist in the initial page HTML. Scope: client and server islands Confidence: high · Verified: Template directives reference

Một component không hydrate cũng không đại diện cho toàn bộ JavaScript trên trang. “Zero JS” chỉ mô tả component không có directive client:*; Astro vẫn có thể gửi thẻ <script> cấp trang, router View Transitions và island khác trên cùng trang. Hãy mô tả JavaScript theo từng component hoặc route, không tuyên bố chung cho toàn trang.

Những gì Astro KHÔNG tự động làm

Astro tạo HTML semantic sạch — và không tự thêm hạng mục SEO nào khác. Mặc định không có metadata, canonical, sitemap hay dữ liệu có cấu trúc. “Astro tự động được tối ưu SEO” chỉ là ngộ nhận. Bạn chịu trách nhiệm cho:

  • Meta tag — title, description, Open Graph, Twitter
  • URL canonical
  • Sitemap qua integration chính thức
  • Dữ liệu có cấu trúc / JSON-LD
  • robots.txt

Astro là nền tảng tốt nhất tôi từng dùng, nhưng vẫn chỉ là nền móng chứ chưa phải căn nhà hoàn thiện.

Sitemap: @astrojs/sitemap

Cài bằng npx astro add sitemap. Integration này thu thập route được tạo static và xuất sitemap-index.xml cùng các tệp phân đoạn sitemap-0.xml tại thời điểm build. Hai điểm thường gây lỗi:

Evidence for this claim Astro's official sitemap integration generates sitemap files from statically generated routes. Scope: Astro @astrojs/sitemap integration. Confidence: high · Verified: Astro: Sitemap integration
  • Phải đặt site: trong astro.config.mjs. Nếu thiếu, integration âm thầm không tạo gì. Đây là nguyên nhân phổ biến nhất của câu hỏi “sitemap của tôi đâu?”.
  • Phải tự thêm dòng sitemap vào robots.txt. Astro không làm việc này.

Để kiểm soát, filter() loại route — chẳng hạn trang preview hoặc draft như tôi dùng trên website này; serialize() cho phép đặt lastmod/changefreq/priority; tùy chọn i18n tạo entry hreflang trong sitemap. Đây là hành vi tài liệu @astrojs/sitemap hiện hành mô tả; nếu dùng phiên bản cũ, hãy kiểm tra đúng bản đã cài vì hành vi từng thay đổi giữa các major version.

Phạm vi dễ gây nhầm: cơ chế discovery của integration nhắm tới route được tạo static. Nếu URL chỉ tồn tại lúc runtime — route render server với output: 'server' hoặc route tạo theo yêu cầu thay vì lúc build — đừng mặc định rằng nó có trong sitemap. Hãy thêm rõ bằng customPages, rồi mở sitemap-index.xml sau build để xác nhận. Không nên tin rằng “integration tự xử lý” đối với route không được tạo static ở build time.

Meta tag và canonical: mẫu BaseLayout

Astro không có component <Head> đặc biệt; bạn điều khiển trực tiếp <head> trong tệp .astro. Mẫu tiêu chuẩn tôi dùng là một BaseLayout.astro nhận title, descriptioncanonicalURL qua props rồi viết phần head. Đặt canonical rõ trên từng trang và giữ nhất quán với og:url. Bạn không cần thư viện, nhưng package cộng đồng astro-seo trên npm là wrapper tiện lợi cho title, description, Open Graph, Twitter và canonical.

Content Collections như lớp an toàn SEO

Đây là tính năng SEO bị đánh giá thấp của Astro. Content Collections cung cấp lớp nội dung type-safe cho Markdown, MDX hoặc JSON với xác thực schema bằng Zod. Bạn có thể bắt buộc titledescription; nếu trang thiếu trường, build sẽ thất bại, nên không thể vô tình phát hành trang không có title. Các hàm getCollection()getEntry() tạo trang static lúc build, vì vậy output triển khai là HTML thuần. MDX giữ Markdown thô làm nguồn chân lý, nên các tệp cũng là nguồn sạch cho crawler AI và mô hình llms.txt. Chính website này dùng Content Collections với frontmatter được Zod xác thực.

astro:assets: xử lý hình ảnh đúng cách — với một bẫy

Component <Image /> tự chuyển sang WebP, suy ra kích thước để “avoid Cumulative Layout Shift (CLS)” (bản dịch) “tránh Cumulative Layout Shift — CLS”, đặt loading="lazy" theo mặc định và bắt buộc có alt; thiếu alt là lỗi compile. <Picture /> mở rộng bằng các phần tử <source> AVIF, WebP và fallback.

Bẫy nằm ở loading="lazy" mặc định, vì nó sai với hình LCP, thường là hero. Lazy-load hình quan trọng nhất sẽ trì hoãn request. Với hình trên màn hình đầu tiên, hãy dùng loading="eager"fetchpriority="high". Hình remote cần chỉ rõ widthheight.

Server Islands: crawler thực sự nhìn thấy gì

Server Islands từ Astro 4.12 trở lên là tính năng nhiều hướng dẫn mô tả sai. Với server:defer, một component được render trên server độc lập với trang chính. Static shell được trả ngay; tài liệu Astro nói: “Your page will be rendered immediately with any specified fallback content as a placeholder. Then, the component’s own contents are fetched on the client and displayed when available.” (bản dịch) “Trang được render ngay với nội dung fallback đã chỉ định làm placeholder. Sau đó, nội dung riêng của component được fetch phía client và hiển thị khi sẵn sàng.”

Cần nêu chính xác hai điểm. Thứ nhất, Server Islands cần adapter; đây là tính năng theo yêu cầu, không phải thứ build static thuần tự tạo. Thứ hai, document ban đầu chứa nội dung fallback đã cấu hình, còn nội dung thật của island được tải bằng request riêng, độc lập sau khi trang tải qua endpoint của nó. Đó là giới hạn chắc chắn của document đầu tiên; không nên suy rộng crawler cụ thể sẽ làm gì tiếp theo nếu chưa kiểm tra URL đã triển khai.

Hệ quả SEO rất cụ thể: HTML static crawler đọc ở lần fetch đầu chứa fallback, không chứa nội dung deferred của island. Điều này phù hợp với dữ liệu cá nhân hóa theo phiên như trạng thái đăng nhập, số lượng giỏ hàng hoặc đề xuất — những thứ không nên cache hay lập chỉ mục. Nó không phù hợp với nội dung chính cần xếp hạng. Hãy đặt nội dung cần lập chỉ mục trong template Astro chính và dùng Server Islands cho phần động bao quanh.

Chế độ output: static, server và override theo route

Chế độ output mặc định của Astro là static: trang và endpoint được prerender thành HTML tại build time. Đặt output: 'server' trong astro.config.mjs sẽ chuyển mặc định sang render theo request bằng adapter, hữu ích cho authentication, dữ liệu real-time hoặc cá nhân hóa vượt quá Server Islands. Có thể override theo route: trong dự án mặc định static, export const prerender = false chuyển route sang on-demand; trong dự án mặc định server, export const prerender = true chuyển route về prerender lúc build. Vì vậy, “website của tôi là static” hay “website của tôi là SSR” hiếm khi đúng cho mọi route; hãy kiểm tra cấu hình từng route, không chỉ config cấp cao nhất.

Evidence for this claim Astro uses static output and prerenders routes at build time by default. Scope: Astro default output mode; routes can opt out of prerendering. Confidence: high · Verified: Astro: On-demand rendering

Xét thuần SEO, HTML prerender và HTML on-demand tương đương sau khi bạn xác nhận route trả mã 200 cùng markup đầy đủ; cả hai đưa HTML hoàn chỉnh cho request đầu tiên của crawler. Route on-demand có thể stream HTML, và dữ liệu hay mạng chậm có thể trì hoãn chunk sau, nên phản hồi stream không tự chứng minh mọi nội dung đã tới. Khác biệt thật sự là vận hành: nội dung prerender cố định tới lần build tiếp theo hoặc cơ chế refresh riêng và được phục vụ từ CDN edge; nội dung on-demand luôn mới nhưng phụ thuộc adapter cùng runtime production. Hãy chọn theo độ mới dữ liệu và vận hành, rồi xác thực route, status, redirect cùng header đã triển khai thay vì suy ra từ local dev.

View Transitions: an toàn cho SEO dù tạo cảm giác SPA

<ClientRouter /> của Astro — trước đây là <ViewTransitions /> — tạo soft navigation giống SPA bằng View Transitions API và History API của trình duyệt. Điểm cốt lõi là nó điều hướng bằng history.pushState, đúng cách Google khuyến nghị cho điều hướng phía client; Google cảnh báo URL fragment #hash là thứ hệ thống “can’t reliably resolve” (bản dịch) “không thể phân giải một cách đáng tin cậy”. View Transitions chỉ là lớp cải tiến phía trình duyệt. Khi crawl, Googlebot request từng URL và nhận trang HTML đầy đủ bình thường; MPA nền không thay đổi. Transition chỉ tác động trải nghiệm của người dùng trong trình duyệt.

Vì vậy, View Transitions không biến website Astro thành SPA và không làm hỏng SEO. Một khoảng trống đáng chú ý là tài liệu View Transitions của Astro không có phần SEO, có thể khiến ngộ nhận tiếp diễn. Để xác minh website của bạn, hãy fetch trực tiếp vài URL và kiểm tra từng URL trả HTML đầy đủ; đừng chỉ tin mặc định.

Lỗi Astro SEO thường gặp

  1. Giả định Astro xử lý SEO thay bạn. Astro xử lý HTML; meta, canonical, sitemap và schema là phần bạn phải làm.
  2. Quên site: trong config. Sitemap sẽ âm thầm không được tạo.
  3. Không thêm sitemap vào robots.txt. Astro không tự thêm.
  4. Lazy-load hình LCP. Hãy đặt hero thành eager cùng fetchpriority.
  5. Đặt nội dung cần lập chỉ mục trong Server Island. Crawler thấy fallback chứ không phải nội dung, và Server Islands cần adapter.
  6. Theo đuổi điểm Lighthouse hoàn hảo rồi dừng lại. Tốc độ là một tín hiệu xếp hạng, không phải tín hiệu duy nhất. Trang rỗng dù nhanh vẫn không xếp hạng; nội dung, liên kết và E-E-A-T vẫn làm phần việc chính.
  7. Đặt nội dung cần lập chỉ mục chỉ trong client:only. Directive này bỏ qua render server, nên component không có HTML cho tới khi trình duyệt hydrate.
  8. Coi “Astro static/nhanh” là bảo đảm kết quả. HTML static hoặc on-demand, islands và adapters chỉ là cơ chế; chúng không tự bảo đảm khả năng crawl, lập chỉ mục, thứ hạng hay Core Web Vitals. Hãy xác thực route đã triển khai.

Vị trí trong cụm chủ đề

Astro là câu trả lời cụ thể, thân thiện với SEO cho các câu hỏi render mà JavaScript SEO đặt ra, đồng thời là frontend phổ biến cho hệ thống headless CMS. Mặt hiệu suất liên hệ trực tiếp với Core Web Vitals trong cụm web performance, còn kỷ luật kiểm tra “nội dung có thật sự nằm trong HTML không?” giống với các cụm crawl và lập chỉ mục.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.