Speculation Rules API — Điều hướng web nhanh như chớp với Prefetch và Prerender

Posted on: 4/18/2026 1:09:29 PM

Bạn đã bao giờ click vào một link và trang mới hiện ra ngay lập tức — không loading, không flash trắng, không chờ đợi? Đó không phải phép thuật, mà là Speculation Rules API đang hoạt động. Đây là một trong những API trình duyệt mạnh mẽ nhất dành cho web performance, cho phép trang web "đoán trước" người dùng sẽ đi đâu tiếp theo và tải sẵn trang đó trước khi họ click.

~0msLCP khi prerender thành công
50Prefetch đồng thời tối đa
10Prerender đồng thời tối đa
Chrome 109+Hỗ trợ từ phiên bản

1. Speculation Rules API là gì?

Speculation Rules API là API thay thế cho <link rel="prerender"> đã bị deprecated. Nó cho phép developer khai báo các quy tắc suy đoán (speculation rules) bằng JSON, chỉ định những URL nào nên được prefetch (tải trước response) hoặc prerender (render sẵn hoàn toàn trong tab ẩn).

Khi người dùng thực sự navigate đến trang đã được prerender, trình duyệt chỉ cần swap tab ẩn thành tab chính — kết quả là trang hiện ra gần như tức thì.

flowchart LR
    A["👤 User đang đọc
trang hiện tại"] --> B["📋 Speculation Rules
phân tích links"] B --> C{"Prefetch hay
Prerender?"} C -->|Prefetch| D["📥 Tải response body
(không render)"] C -->|Prerender| E["🖥️ Render hoàn toàn
trong tab ẩn"] D --> F["⚡ User click →
Tải nhanh hơn"] E --> G["⚡ User click →
Hiển thị tức thì"] style A fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style B fill:#e94560,stroke:#fff,color:#fff style C fill:#2c3e50,stroke:#fff,color:#fff style D fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50 style E fill:#4CAF50,stroke:#fff,color:#fff style F fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style G fill:#e94560,stroke:#fff,color:#fff
Luồng hoạt động của Speculation Rules API

2. Prefetch vs Prerender — Chọn đúng chiến lược

Hai chiến lược này khác nhau đáng kể về chi phí và hiệu quả:

Tiêu chíPrefetchPrerender
Hoạt độngTải response body (HTML)Tải + render hoàn toàn trong tab ẩn
Subresources (CSS, JS, ảnh)Không tảiTải tất cả
JavaScriptKhông executeExecute đầy đủ
Chi phí bộ nhớThấpCao (tương đương 1 iframe)
Tốc độ cải thiệnTrung bình — tiết kiệm network round-tripGần tức thì — trang đã sẵn sàng
Cross-originSame-site (không cookies)Same-origin mặc định
CacheIn-memory, 5 phútIn-memory, 5 phút
Nên dùng khiNhiều trang có thể được truy cậpTrang gần chắc chắn sẽ được truy cập

💡 Quy tắc vàng

Prefetch rộng, prerender hẹp. Prefetch nhiều trang vì chi phí thấp. Prerender chỉ 1-2 trang mà bạn tin chắc (>50%) user sẽ navigate đến.

3. Cú pháp JSON và cách triển khai

Speculation rules được khai báo trong thẻ <script type="speculationrules">:

3.1. List Rules — Chỉ định URL cụ thể

<script type="speculationrules"> { "prerender": [{ "urls": ["/san-pham/hot-deal", "/gio-hang"] }], "prefetch": [{ "urls": ["/blog", "/lien-he", "/gioi-thieu"] }] } </script>

List rules phù hợp khi bạn biết chính xác URL nào cần tải trước — ví dụ trang giỏ hàng trên e-commerce, hoặc bài viết tiếp theo trên blog.

3.2. Document Rules — Tự động dựa trên selector và URL pattern

<script type="speculationrules"> { "prerender": [{ "where": { "and": [ { "href_matches": "/*" }, { "not": { "href_matches": "/admin/*" } }, { "not": { "href_matches": "/logout" } }, { "not": { "href_matches": "/*?*(^|&)add-to-cart=*" } }, { "not": { "selector_matches": ".no-prerender" } }, { "not": { "selector_matches": "[rel~=nofollow]" } } ] }, "eagerness": "moderate" }] } </script>

Document rules mạnh mẽ hơn vì tự động áp dụng cho mọi link trên trang mà match điều kiện. Bạn không cần liệt kê từng URL — chỉ cần loại trừ những URL không nên prerender.

3.3. Khai báo qua HTTP Header

Từ Chrome 121+, bạn có thể serve speculation rules qua HTTP header thay vì inline script:

Speculation-Rules: "/speculationrules.json"

File JSON phải được serve với MIME type application/speculationrules+json và pass CORS check nếu cross-origin.

4. Eagerness — Kiểm soát thời điểm kích hoạt

Thuộc tính eagerness quyết định khi nào trình duyệt bắt đầu speculation:

EagernessDesktopMobilePhù hợp cho
immediateNgay khi rules được parseURL chắc chắn (homepage → dashboard)
eagerHover 10msLink vào viewport 50msNavigation chính trong menu
moderateHover 200ms hoặc pointerdownScroll dừng + viewport heuristic 500msLink trong nội dung bài viết
conservativePointer down / touch startTrang nặng, cần tiết kiệm tài nguyên

Mặc định

List rules mặc định immediate — tải ngay khi parse. Document rules mặc định conservative — chỉ tải khi user bắt đầu tương tác (pointer down). Đa số trường hợp nên dùng moderate cho document rules để cân bằng tốc độ và tài nguyên.

flowchart TD
    A["Speculation Rules
được parse"] --> B{"Eagerness?"} B -->|immediate| C["Tải ngay lập tức"] B -->|eager| D["Hover 10ms (Desktop)
Viewport 50ms (Mobile)"] B -->|moderate| E["Hover 200ms (Desktop)
Scroll stop 500ms (Mobile)"] B -->|conservative| F["Pointer down /
Touch start"] C --> G["Prefetch / Prerender
bắt đầu"] D --> G E --> G F --> G style A fill:#e94560,stroke:#fff,color:#fff style B fill:#2c3e50,stroke:#fff,color:#fff style C fill:#4CAF50,stroke:#fff,color:#fff style D fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50 style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style F fill:#f8f9fa,stroke:#ff9800,color:#2c3e50 style G fill:#e94560,stroke:#fff,color:#fff
Các mức eagerness và thời điểm kích hoạt

5. Giới hạn và cơ chế bảo vệ của Chrome

Chrome đặt ra các giới hạn để tránh lạm dụng tài nguyên:

EagernessPrefetch tối đaPrerender tối đaHành vi khi đầy
immediate / eager5010Từ chối thêm mới
moderate / conservative22FIFO — huỷ cũ nhất

Ngoài ra, Chrome sẽ tự động chặn speculation khi:

  • Save-Data được bật — user đang tiết kiệm dữ liệu
  • Energy Saver mode với pin yếu
  • Bộ nhớ thấp — thiết bị không đủ RAM
  • "Preload pages" bị tắt trong Chrome Settings
  • Tab ở background — không lãng phí tài nguyên cho tab không nhìn thấy

⚠️ Prerender bị huỷ khi gặp

Các trang có alert(), confirm(), prompt(), yêu cầu geolocation, camera, hoặc notification sẽ bị huỷ prerender. Các API này được deferred (tạm dừng) cho đến khi trang thực sự activate. Tránh đặt chúng trong quá trình load ban đầu.

6. Phát hiện và đo lường hiệu quả

6.1. Feature Detection

if (HTMLScriptElement.supports && HTMLScriptElement.supports("speculationrules")) { // Trình duyệt hỗ trợ Speculation Rules const specScript = document.createElement("script"); specScript.type = "speculationrules"; specScript.textContent = JSON.stringify({ prefetch: [{ urls: ["/next-page"] }] }); document.body.append(specScript); } else { // Fallback: dùng <link rel="prefetch"> const link = document.createElement("link"); link.rel = "prefetch"; link.href = "/next-page"; document.head.append(link); }

6.2. Server-side Detection qua Sec-Purpose Header

Request từ speculation sẽ kèm header Sec-Purpose:

// Prefetch request Sec-Purpose: prefetch // Prerender request Sec-Purpose: prefetch;prerender

Server có thể dùng header này để bỏ qua side-effects — ví dụ không ghi analytics, không trigger email notification cho prefetch/prerender request.

6.3. Client-side Detection

// Kiểm tra trang có đang trong trạng thái prerendering? if (document.prerendering) { // Defer analytics, mutations... document.addEventListener("prerenderingchange", () => { // Trang đã activate — chạy analytics gtag("event", "page_view", { prerendered: true }); }, { once: true }); } // Kiểm tra trang có được prefetch không? const navEntry = performance.getEntriesByType("navigation")[0]; if (navEntry.deliveryType === "navigational-prefetch") { console.log("Trang này được tải từ prefetch cache"); } // Đo activation time cho prerender if (navEntry.activationStart > 0) { const prerenderTime = navEntry.activationStart; console.log(`Prerender activation sau ${prerenderTime}ms`); }

7. Xử lý các tình huống nguy hiểm

Không phải URL nào cũng nên prefetch/prerender. Một số URL có side-effects khi được request:

flowchart TD
    A["🔗 URL cần
speculation"] --> B{"Có side-effect
không?"} B -->|Không| C["✅ An toàn
Prefetch/Prerender"] B -->|Có| D{"Loại
side-effect?"} D -->|Logout/Auth| E["❌ Loại trừ
khỏi rules"] D -->|Analytics| F["⏸️ Defer
qua prerenderingchange"] D -->|Add to cart| G["❌ Loại trừ
khỏi rules"] D -->|Storage mutation| H["⏸️ Defer
qua prerenderingchange"] style A fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style B fill:#2c3e50,stroke:#fff,color:#fff style C fill:#4CAF50,stroke:#fff,color:#fff style D fill:#e94560,stroke:#fff,color:#fff style E fill:#ff9800,stroke:#fff,color:#fff style F fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50 style G fill:#ff9800,stroke:#fff,color:#fff style H fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Quy trình xử lý URL có side-effects

Pattern an toàn cho Analytics

// Đợi trang thực sự được user nhìn thấy rồi mới track function initAnalytics() { gtag("config", "GA_ID"); gtag("event", "page_view"); } if (document.prerendering) { document.addEventListener("prerenderingchange", initAnalytics, { once: true }); } else { initAnalytics(); }

Pattern cho Server-side (ASP.NET Core)

app.Use(async (context, next) => { var secPurpose = context.Request.Headers["Sec-Purpose"].ToString(); if (secPurpose.Contains("prefetch") || secPurpose.Contains("prerender")) { // Bỏ qua side-effects cho speculation request context.Items["IsSpeculation"] = true; } await next(); }); // Trong controller/endpoint: if (HttpContext.Items.ContainsKey("IsSpeculation")) { // Không gửi email, không ghi audit log return View(model); }

8. Triển khai thực tế cho các loại website

8.1. Blog / Content Site

<script type="speculationrules"> { "prerender": [{ "where": { "and": [ { "href_matches": "/blog/*" }, { "not": { "selector_matches": "[rel~=nofollow]" } } ] }, "eagerness": "moderate" }], "prefetch": [{ "where": { "href_matches": "/tag/*" }, "eagerness": "conservative" }] } </script>

8.2. E-commerce

<script type="speculationrules"> { "prerender": [{ "where": { "and": [ { "href_matches": "/san-pham/*" }, { "not": { "href_matches": "/*?*add-to-cart=*" } }, { "not": { "href_matches": "/checkout*" } }, { "not": { "href_matches": "/thanh-toan*" } } ] }, "eagerness": "moderate" }], "prefetch": [{ "urls": ["/gio-hang"], "eagerness": "conservative" }] } </script>

8.3. SPA với Vue.js / Nuxt — Kết hợp cùng Router

// plugins/speculation-rules.client.ts (Nuxt 3) export default defineNuxtPlugin(() => { if (!HTMLScriptElement.supports?.("speculationrules")) return; const router = useRouter(); router.afterEach((to) => { // Xoá rules cũ document.querySelectorAll('script[type="speculationrules"]') .forEach(el => el.remove()); // Tạo rules mới dựa trên route hiện tại const rules = buildRulesForRoute(to.path); if (!rules) return; const script = document.createElement("script"); script.type = "speculationrules"; script.textContent = JSON.stringify(rules); document.body.append(script); }); }); function buildRulesForRoute(path: string) { // Blog: prerender bài tiếp theo if (path.startsWith("/blog/")) { return { prerender: [{ where: { and: [ { selector_matches: ".next-post a, .related-posts a" } ] }, eagerness: "moderate" }] }; } // Trang chủ: prefetch các mục chính if (path === "/") { return { prefetch: [{ where: { selector_matches: ".featured-links a" }, eagerness: "eager" }] }; } return null; }

9. Tác động đến Core Web Vitals

Speculation Rules API cải thiện đáng kể các chỉ số Core Web Vitals khi trang được prerender thành công:

~0msLCP — trang đã render sẵn
0CLS — layout shift xảy ra trước khi user nhìn
Tốt hơnINP — JS đã execute xong, sẵn sàng tương tác

💡 Khi prerender chưa hoàn tất

Nếu user navigate trước khi prerender xong, trang sẽ tiếp tục load ở foreground — vẫn có head-start advantage so với load từ đầu. Không bao giờ chậm hơn trường hợp không có speculation.

10. Tính năng mới: Tag và Target Hint

Tag Field (Chrome 136+)

Gắn label cho rules để server-side filtering qua CDN:

{ "tag": "product-prerender", "prerender": [{ "urls": ["/san-pham/moi-nhat"] }] }

Server nhận header Sec-Speculation-Tags: product-prerender — CDN có thể phân biệt speculation request từ site vs từ browser tự động.

Target Hint (Chrome 138+, experimental)

{ "prerender": [{ "urls": ["/external-page"], "target_hint": "_blank" }] }

Cho phép prerender trang sẽ được mở trong tab mới — hữu ích cho link target="_blank".

11. Content Security Policy

Nếu site dùng CSP, cần thêm directive cho inline speculation rules:

Content-Security-Policy: script-src 'self' 'inline-speculation-rules'

Hoặc dùng nonce/hash như với script thông thường. Nếu không có directive này, inline <script type="speculationrules"> sẽ bị chặn.

12. Browser Support và Progressive Enhancement

Trình duyệtPrefetchPrerenderDocument RulesEagerness
Chrome 109+✅ (121+)✅ (121+)
Edge 109+✅ (121+)✅ (121+)
Firefox
Safari⚠️ Behind flag⚠️ Behind flag

Progressive Enhancement — không lo break

Speculation Rules API là progressive enhancement thuần tuý. Trình duyệt không hỗ trợ sẽ bỏ qua <script type="speculationrules"> hoàn toàn — không lỗi, không ảnh hưởng. Bạn có thể triển khai ngay mà không sợ break trải nghiệm trên Firefox hay Safari.

13. Checklist triển khai

Trước khi đưa Speculation Rules vào production, đảm bảo kiểm tra:

  1. Xác định URL an toàn: Loại trừ logout, checkout, add-to-cart, và mọi URL có side-effect
  2. Chọn eagerness phù hợp: moderate cho phần lớn trường hợp, immediate cho navigation chắc chắn
  3. Defer analytics: Dùng prerenderingchange event để không track prerender dưới dạng page view thật
  4. Kiểm tra Sec-Purpose server-side: Bỏ qua side-effects cho speculation request
  5. Thêm CSP directive: 'inline-speculation-rules' nếu site dùng Content Security Policy
  6. Test trên Chrome DevTools: Kiểm tra tab Application → Speculative loads để xem prerender status
  7. Đo lường: So sánh Core Web Vitals trước/sau bằng CrUX hoặc RUM data

Kết luận

Speculation Rules API đánh dấu bước tiến lớn trong web performance — lần đầu tiên developer có công cụ native, khai báo bằng JSON đơn giản, để đạt được near-instant navigation mà không cần framework phức tạp hay service worker. Với tính chất progressive enhancement, bạn có thể triển khai ngay hôm nay và user trên Chrome/Edge sẽ được hưởng lợi ngay lập tức, trong khi các trình duyệt khác hoạt động bình thường.

Điều quan trọng nhất: prefetch rộng, prerender hẹp, defer side-effects — ba nguyên tắc này đủ để bạn triển khai an toàn và hiệu quả trên mọi loại website.

💡 Bắt đầu từ đâu?

Thêm một thẻ <script type="speculationrules"> đơn giản với document rules + moderate eagerness cho internal links. Chỉ mất 10 dòng code nhưng có thể giảm LCP của navigation tiếp theo xuống gần 0.

Nguồn tham khảo: