Số phiên bản bị đóng băng trong User-Agent có thể không còn đúng. Website suy ra phiên bản engine thật qua feature-detection thế nào.
Chuỗi User-Agent của bạn báo Chrome/143.0.0.0. Điều đó có đúng không? Có hai lý do riêng biệt khiến bạn không thể kiểm chứng nó chỉ bằng chính chuỗi đó. Thứ nhất, Rút gọn User-Agent đã đóng băng toàn bộ phần sau số phiên bản chính thành .0.0.0 cố định, nên mẩu thông tin phiên bản duy nhất từng thay đổi theo mỗi bản cập nhật giờ đã biến mất. Thứ hai — và đây mới là điều quan trọng — cả chuỗi UA vốn dĩ là thứ trình duyệt tự nói về mình, và một tiện ích mở rộng hay một framework tự động hoá chỉ cần một dòng lệnh là viết lại được. Một website thực sự cần biết bạn đang chạy engine ở mốc phiên bản nào phải đặt một câu hỏi hoàn toàn khác: không phải "trình duyệt tự khai là gì", mà là "nó thực sự có những khả năng gì". Đó chính là suy luận phiên bản qua feature-detection — cùng kỹ thuật đứng sau công cụ kiểm tra nhân trình duyệt của chính BrowserInsight.
Tóm tắt nhanh
- Suy luận phiên bản qua feature-detection kiểm tra sự tồn tại của một khả năng đã xuất hiện ở một phiên bản engine nhất định, đồng thời kiểm tra sự vắng mặt của một khả năng chỉ xuất hiện ở phiên bản kế tiếp, từ đó khoanh vùng engine thật vào một khoảng hẹp.
- Kết quả luôn là một khoảng, không phải một con số chính xác — các tính năng ra mắt theo từng đợt, một số bị khoá sau flag thử nghiệm hoặc được backport ngược, nên kỹ thuật này chỉ có thể khoanh vùng một dải phiên bản, chứ không thể cho ra chuỗi build bốn phần mà
chrome://versionhiển thị. - Mọi bộ dò như vậy đều có một trần giới hạn: nó chỉ biết những tính năng đã tồn tại tại thời điểm bảng dò được xây dựng, nên một khi trình duyệt của bạn vượt qua mọi phép thử trong bảng, kết quả trung thực chỉ có thể là "phiên bản này hoặc mới hơn".
- Hai phép đo lệch nhau theo cách không đối xứng: phiên bản dò được thấp hơn mức UA tuyên bố thường chỉ là dấu hiệu bảng dò đã cũ; còn phiên bản dò được cao hơn mức UA tuyên bố mới là hướng thực sự chỉ ra giả mạo, vì trình duyệt có thể giấu phiên bản nhưng không thể diễn được những khả năng nó không có.
- Cũng chính những phép dò đó phân biệt Blink, Gecko và WebKit với nhau từ trước khi nói đến phiên bản, vì tập tính năng của ba engine này phân hoá độc lập với nhau.
Cơ chế: khoanh vùng phiên bản bằng phép dò tính năng
Mỗi lần engine trình duyệt ra bản mới đều mang theo một loạt khả năng nền tảng web có thể quan sát được — một thuộc tính CSS, một phương thức JavaScript, một interface DOM. Phần lớn trong số này có thể được trang web kiểm tra trực tiếp, không cần hộp thoại xin quyền, không cần người dùng tương tác: kiểm tra một thuộc tính có tồn tại không, một constructor đã được định nghĩa chưa, một giá trị CSS có được chấp nhận không.
Một phép dò tồn tại tối giản trông như thế này:
// `Intl.Locale.prototype.variants` ra mắt ở Chromium 149.
const has149 = 'variants' in Intl.Locale.prototype;
// Thuộc tính CSS `text-fit` ra mắt ở Chromium 150.
const has150 = CSS.supports('text-fit', 'initial');
// has149 && !has150 -> engine này chính là Chromium 149.
Cả hai dòng này đều không tải bất cứ thứ gì, không chạm mạng — chúng chỉ hỏi engine một câu hỏi có/không về chính khả năng của nó. Điểm mấu chốt là chọn từng cặp phép dò một cách có chủ đích: một tính năng biết chắc đã ra mắt ở một phiên bản nào đó, và một tính năng biết chắc vẫn chưa có ở phiên bản đó nhưng sẽ có ở phiên bản kế tiếp. Nếu phép dò đầu tiên đạt và phép dò thứ hai không đạt, trình duyệt bị khoanh vào khoảng "là phiên bản này, nhưng chưa tới phiên bản kế tiếp". Chạy đủ nhiều cặp phép dò như vậy theo một bảng các mốc phát hành đã biết, bạn có thể leo dần lên cho tới khi tìm ra phiên bản cao nhất mà trình duyệt thoả mãn hoàn toàn — đó chính là ước lượng cận dưới của phiên bản engine thật, hoàn toàn độc lập với những gì chuỗi UA tuyên bố.
Đây chính xác là cách công cụ kiểm tra nhân trình duyệt của BrowserInsight hoạt động: nó duyệt qua một bảng các mốc phiên bản Chromium (bắt đầu từ Chromium 79) và một bảng riêng cho Firefox, kiểm tra từng chênh lệch tính năng đã biết của mỗi mốc, rồi báo cáo phiên bản được suy ra từ điểm mà chuỗi "đạt" bị đứt — cùng lúc hiển thị phiên bản mà User-Agent tuyên bố, để bạn thấy trực tiếp liệu hai bên có khớp nhau không. Hai phép dò ở trên được lấy thẳng từ chính bảng đó.
Vì sao câu trả lời là một khoảng, không phải một con số
Ma trận feature-detection không bao giờ có thể chỉ ra bản build chính xác theo cách chrome://version làm được — nó chỉ có thể khoanh vùng một mốc phiên bản, và ngay cả điều đó cũng đi kèm vài lưu ý cần nói rõ từ đầu:
- Tính năng ra mắt theo từng đợt, không phải một tính năng một phiên bản, nên một phép thử đạt riêng lẻ hiếm khi tự nó xác định được một phiên bản cụ thể — thường cần vài phép thử xác nhận và loại trừ kết hợp lại mới thu hẹp được khoảng.
- Một số tính năng bị khoá sau flag thử nghiệm trong một hoặc vài phiên bản trước khi mở mặc định cho tất cả, nên một phần nhỏ người dùng đang ở đúng phiên bản đó nhưng chưa được rollout tính năng có thể trượt một phép thử "lẽ ra phải đạt".
- Backport thực sự xảy ra: một tính năng liên quan bảo mật đôi khi được đưa ngược vào một kênh ổn định cũ hơn, nằm ngoài nhịp phát hành thông thường, khiến một bản build cũ trông "mới hơn thực tế" ở đúng phép thử đó.
Không điều nào trong số này làm hỏng kỹ thuật — chúng chỉ có nghĩa là kết quả trung thực phải là "trình duyệt này ít nhất là phiên bản N", chứ không phải "trình duyệt này chính xác là phiên bản N.N.N.N". Bất kỳ công cụ nào, kể cả công cụ kiểm tra nhân trình duyệt của chính trang này, nếu trình bày feature-detection như thể nó giải ra được số phiên bản vá chính xác, thì đều đang thổi phồng những gì phương pháp này thực sự làm được.
Vì sao mọi bộ dò đều có trần giới hạn
Một bảng feature-detection về bản chất là một tấm ảnh chụp: nó chỉ có thể kiểm tra những khả năng đã tồn tại và được biết đến tại thời điểm bảng được xây dựng. Một khi trình duyệt vượt qua mọi phép thử trong bảng — vì nó thực sự mới hơn mốc phiên bản mới nhất mà bảng bao phủ — thì không còn phép thử nào có thể giữ nó lại, và báo cáo trung thực chỉ có thể là "phiên bản này hoặc mới hơn", chứ không phải một con số chính xác. Đây không phải lỗi của riêng một cách cài đặt nào, mà là giới hạn mang tính cấu trúc của chính phương pháp — và điều này áp dụng như nhau cho cả công cụ kiểm tra nhân trình duyệt của trang này. Bảng dò cần được cập nhật định kỳ khi engine liên tục ra phiên bản mới và các tính năng phân biệt mới xuất hiện để kiểm tra — lộ trình tính năng của chính Chrome và danh sách tính năng đã phát hành là nguồn công khai chính thức ghi lại những gì đã ra mắt và khi nào, và đó chính xác là thứ người duy trì một bảng như vậy phải theo dõi liên tục. Một điểm đối chiếu chéo hữu ích cho dòng thời gian hỗ trợ của từng tính năng riêng lẻ giữa các engine là caniuse.com, đó cũng là lý do người duy trì thường không chỉ dựa vào changelog của riêng một hãng trình duyệt.
Đọc chênh lệch theo đúng chiều
Khi đã có hai phép đo, việc hiển nhiên tiếp theo là đem ra so. Nhưng hai kiểu chênh lệch không hề ngang giá nhau, và coi chúng là đối xứng chính là cách một công cụ kiểm tra phiên bản bắt đầu gán nhãn giả mạo cho người dùng bình thường.
- Phiên bản dò được thấp hơn mức UA tuyên bố thường hoàn toàn không phải nói dối. Đó chính là cái trần ở phần trước hiện ra trong thực tế: trình duyệt mới hơn bảng dò, vượt qua mọi phép thử trong bảng, nên bị báo cáo ở nấc cao nhất của bảng. Công cụ kiểm tra nhân trình duyệt của BrowserInsight cố tình không đưa ra kết luận giả mạo cho trường hợp này — với bất kỳ bảng dò nào không được cập nhật ngay trong tuần một mốc phiên bản mới ra mắt, kết luận đó sẽ bắn nhầm vào mọi khách truy cập đang dùng bản mới nhất.
- Phiên bản dò được cao hơn mức UA tuyên bố mới là chiều thực sự đáng ngờ. Trình duyệt có thể chọn không công bố phiên bản của mình, nhưng không thể trình diễn những khả năng nó không có. Các tính năng chỉ tồn tại ở mốc phiên bản cao hơn, chạy dưới một chuỗi UA khai mốc thấp hơn, có nghĩa là chuỗi đó đã bị viết lại trong khi engine bên dưới vẫn nói thật.
Còn một mẫu hình thứ ba hoàn toàn không dính đến UA: kết quả đạt/trượt có tạo thành một bậc thang sạch sẽ hay không. Một engine thật sẽ đạt mọi mốc cho tới đúng phiên bản của nó và trượt toàn bộ những mốc cao hơn, không để lỗ hổng ở giữa. Đạt một mốc cao trong khi trượt một mốc thấp hơn thì đó không còn là một phiên bản nào cả — đó là hình dạng mà không bản build thật nào tạo ra, và nó chỉ tới một môi trường bị vá hoặc giả lập, chứ không phải một trình duyệt lỗi thời. Vì phép kiểm tra này chỉ đối chiếu các kết quả dò với nhau, nó vẫn hiệu lực ngay cả khi chuỗi UA vắng mặt, chung chung hay hoàn toàn vô nghĩa.
Đa engine: cùng một kỹ thuật, trước cả khi nói đến phiên bản
Feature-detection không chỉ dùng để khoanh vùng phiên bản — đó cũng là cách một trang phân biệt các engine với nhau từ trước khi phiên bản được nhắc tới. Blink, Gecko và WebKit đã phân hoá từ lâu về thời điểm và thứ tự họ đưa các tính năng thử nghiệm và mới được chuẩn hoá vào sản phẩm, sự phân hoá này bắt đầu từ rất lâu trước khi mỗi engine đạt tới phiên bản hiện tại của nó. Một tính năng tồn tại trong Blink nhưng không có trong Gecko đủ để phân biệt hai engine này, bất kể phiên bản Chromium hay Firefox cụ thể nào đang chạy. Để hiểu sâu hơn vì sao ba engine phân hoá theo cách này, xem Giải thích engine trình duyệt: Blink, Gecko và WebKit; để phân biệt engine dựng hình mà kỹ thuật này dò, với engine JavaScript riêng biệt đi kèm nó, xem Engine dựng hình vs engine JavaScript.
Cũng cần nói rõ chính xác điều gì đang được phát hiện. Chrome, Edge, Brave, Opera và mọi trình duyệt khác dựa trên Chromium đều dùng chung một engine Blink bên dưới, nên phép dò feature-detection báo cáo phiên bản của engine dùng chung đó — "Chromium 143" — chứ không phải sản phẩm mang thương hiệu bọc bên ngoài nó. Chromium và Chrome giải thích chi tiết vì sao sự phân biệt này tồn tại và vì sao đó không phải lỗi, còn Độc quyền Chromium giải thích vì sao nhiều hãng trình duyệt không liên quan đến nhau lại cùng chọn dùng chung một engine.
Vì sao kỹ thuật này càng ngày càng hữu ích, chứ không kém đi
Feature-detection không phải một giải pháp tạm từng hữu dụng rồi lỗi thời khi có phương án tốt hơn — khi các tín hiệu phiên bản khác thay đổi, nó trở nên giá trị hơn, chứ không kém đi:
- Chuỗi User-Agent trước đây từng thay đổi theo mỗi bản cập nhật thật. Trước Rút gọn User-Agent, UA của trình duyệt báo một con số gần với phiên bản vá thật, nên việc suy ra phiên bản từ tính năng lúc đó chủ yếu chỉ là một điều thú vị mang tính kỹ thuật. Giờ đây các chữ số phiên bản trong UA được cố định ở
.0.0.0theo thiết kế, hoàn toàn không đổi giữa các bản vá — nó vốn được thiết kế để trông tĩnh, và với mọi bản cài cập nhật đầy đủ thì đúng là như vậy. Feature-detection là tín hiệu phiên bản duy nhất còn lại chưa bị đưa về không. - User-Agent Client Hints, cụ thể là header
Sec-CH-UA-Full-Version-Listvà APInavigator.userAgentDatađi kèm, đã đưa số phiên bản chính xác trở lại — nhưng đó chỉ là cách thể hiện thứ ba của cùng một lời tự khai báo từ trình duyệt, được đặt sau một yêu cầu tường minh thay vì phát mặc định. Đây thực sự là một kênh hữu ích, chi tiết hơn, nhưng nó cũng dễ bị giả mạo bởi đúng những công cụ có thể giả mạo UA, vì cả hai đều xuất phát từ việc trình duyệt tự khai báo về chính nó. - Sự bất đồng giữa các phép đo giờ là một tín hiệu thật. Khi có ba nguồn tương đối độc lập — UA bị đóng băng, phiên bản do Client Hints tuyên bố, và phiên bản suy ra qua feature-detection — một trình duyệt tuyên bố cùng một điều trên cả hai kênh tự khai báo nhưng lại hành xử như một phiên bản engine hoàn toàn khác chính là kiểu mâu thuẫn được nói tới trong bài phát hiện giả mạo User-Agent. Feature-detection là phép đo duy nhất trong ba phép đo đó mà trình duyệt không thể tự ý tuyên bố.
Tự kiểm tra trên trình duyệt của bạn
Công cụ kiểm tra nhân trình duyệt của BrowserInsight chạy ngay phép dò này trên trình duyệt của bạn: nó hiển thị phiên bản suy ra qua feature-detection cạnh phiên bản mà User-Agent tuyên bố, kèm kết luận liệu hai bên có khớp nhau không, và liệt kê đầy đủ kết quả đạt/trượt theo từng mốc để bạn thấy bậc thang của mình dừng ở đâu.
Nếu muốn tự mắt xem kỹ thuật này trước, hãy mở DevTools và chạy CSS.supports('text-fit', 'initial'). Trả về true nghĩa là bạn đang ở Chromium 150 trở lên; trả về false nghĩa là bạn hoặc thấp hơn mốc đó, hoặc đang ở một engine khác hẳn — dù là trường hợp nào, đó cũng là một nấc thang đọc thẳng từ engine, hoàn toàn không qua User-Agent.
Câu hỏi thường gặp
Feature-detection có tiết lộ được số build Chrome chính xác của tôi không?
Không. Nó có thể khoanh engine của bạn về đúng một mốc phiên bản — chẳng hạn "ít nhất là Chromium 149, nhưng chưa tới 150" — nhưng các chữ số build và bản vá ở đuôi mà chrome://version cho thấy thì luôn nằm ngoài tầm với. Tính năng ra mắt theo từng mốc phiên bản, không theo từng bản vá, nên độ chính xác cấp bản vá vốn dĩ không phải điều kỹ thuật này có thể cung cấp.
Cách này có hoạt động y hệt với Firefox và Safari không?
Logic khoanh vùng bản thân nó không phụ thuộc engine, nhưng bảng tính năng phải được xây dựng và duy trì riêng cho từng engine. Công cụ kiểm tra nhân trình duyệt của BrowserInsight chạy một bảng cho Chromium (Blink) và một bảng cho Firefox (Gecko); cách đánh số phiên bản của WebKit nhiễu hơn nhiều và hiện trang này chưa có ma trận tương ứng, dù bước nhận diện engine tương tự vẫn áp dụng được cho nó.
Nếu một trình duyệt trượt mọi phép thử trong bảng, điều đó có nghĩa nó là trình duyệt cũ không?
Không hẳn — điều đó cũng có thể có nghĩa trình duyệt thực sự mới hơn mốc phiên bản mới nhất mà bảng biết đến, nên không có gì trong bảng phân biệt được nó với cái trần đó. Đạt mọi phép thử và trượt mọi phép thử trông giống hệt nhau trước một bảng dò đã lỗi thời; chỉ có mẫu hình cụ thể của các phép thử đạt/trượt (chứ không phải đạt sạch hay trượt sạch) mới cho bạn biết bạn đang ở phía nào của cái trần đó.
Vì sao không tin luôn phiên bản từ User-Agent Client Hints?
Client Hints thực sự cho một số phiên bản chính xác, nhưng đó vẫn là trình duyệt tự phát biểu về chính nó qua một kênh khác — không phải hành vi được xác minh độc lập. Feature-detection tương đối khó giả mạo thuyết phục hơn, vì để giả mạo nó, trình duyệt phải thực sự triển khai (hoặc giả lập đủ thuyết phục) từng tính năng trong bảng cho tới đúng mốc phiên bản được tuyên bố, chứ không chỉ trả về một chuỗi khác.
Kết luận
Một phép dò feature-detection trả lời câu hỏi mà chuỗi User-Agent bị đóng băng không còn thể trả lời trung thực nữa: trình duyệt này thực sự đang chạy engine phiên bản nào, dựa trên những gì nó thực sự làm được, chứ không phải những gì nó tự nhận. Câu trả lời luôn là một khoảng có trần giới hạn, chứ không phải một số build chính xác — đó là giới hạn trung thực của chính phương pháp, không phải khiếm khuyết của riêng một cách cài đặt nào. Giá trị của nó không nằm ở độ chính xác, mà ở tính độc lập. Trong tất cả những gì trình duyệt tự khai báo về mình, đây là tín hiệu duy nhất mà trình duyệt không thể tự ý tuyên bố — và chính vì vậy nó đáng để đối chiếu với mọi thứ khác mà trình duyệt nói về chính nó.
Đọc thêm:


