Tại sao việc "chuẩn hóa" dấu gạch chép kép // trong đường dẫn HTTP là sai lầm

18 tháng 4, 2026·5 phút đọc

Nhiều nhà phát triển lầm tưởng rằng việc gộp hai dấu gạch chép // thành một là một phần của chuẩn hóa URL, nhưng thực tế điều này vi phạm tiêu chuẩn RFC 3986. Dấu // đại diện cho một đoạn đường dẫn rỗng hợp lệ và việc thay đổi nó có thể làm thay đổi định danh tài nguyên, gây ra lỗi trong các hệ thống như Git.

Trong quá trình phát triển web hoặc cấu hình máy chủ, nhiều người có thói quen tự động "dọn dẹp" các URL bằng cách gộp hai dấu gạch chép (//) thành một (/). Họ coi đây là một bước chuẩn hóa để làm cho đường dẫn gọn gàng hơn. Tuy nhiên, theo các tiêu chuẩn kỹ thuật chính thức của HTTP và URI, hành động này là hoàn toàn sai lầm và có thể dẫn đến những hậu quả nghiêm trọng.

Bài viết này sẽ giải thích lý do tại sao // lại có ý nghĩa quan trọng trong cú pháp đường dẫn và tại sao chúng ta không nên tự ý thay đổi nó.

Tiêu chuẩn RFC 3986 cho phép các đoạn rỗng

Theo RFC 3986 - tiêu chuẩn định nghĩa cú pháp URI (Uniform Resource Identifier), thành phần đường dẫn (path) được cấu tạo từ các đoạn (segment) được phân tách bởi dấu gạch chép /.

Cú pháp chính quy định rằng một đoạn có thể là chuỗi rỗng (segment = *pchar). Điều này có nghĩa là một đường dẫn như /a//b thực chất bao gồm ba đoạn: "a", "" (rỗng), và "b". Do đó, việc xuất hiện dấu gạch chép kép là hợp lệ về mặt cú pháp và nó đại diện cho một đoạn có độ dài bằng không nằm giữa hai dấu phân cách.

Bất kỳ sự chuyển đổi nào tự động gộp // thành / đều đang xóa bỏ một đoạn hợp lệ, từ đó làm thay đổi trình tự các đoạn đã được phân tích cú pháp (parsed sequence).

HTTP sử dụng ngữ pháp đường dẫn của RFC 3986

Giao thức HTTP (được định nghĩa trong RFC 9110) sử dụng ngữ pháp đường dẫn từ RFC 3986 để xác định mục tiêu yêu cầu (request target). Trong HTTP, thành phần đường dẫn phân cấp được sử dụng để xác định tài nguyên trong không gian tên của máy chủ gốc.

Vì các đoạn đường dẫn (trừ các đoạn dot-segments như ...) được coi là "mờ" (opaque) đối với cú pháp chung, việc thay đổi chuỗi các đoạn này đồng nghĩa với việc thay đổi định danh của tài nguyên. Trừ khi máy chủ gốc (origin server) quy định rõ rằng hai định danh này là tương đương, một bộ chuẩn hóa (normalizer) chung không có quyền tự ý sửa đổi chúng.

Các quy tắc chuẩn hóa không bao gồm gộp dấu gạch chép

RFC 3986 rất rõ ràng về những gì được phép trong "chuẩn hóa dựa trên cú pháp". Các kỹ thuật được chấp nhận bao gồm:

  • Chuẩn hóa chữ hoa/thường (case normalization).
  • Chuẩn hóa mã hóa phần trăm (percent-encoding normalization).
  • Loại bỏ các đoạn dot-segments (...).

Tiêu chuẩn không liệt kê bất kỳ quy tắc nào cho phép loại bỏ các đoạn rỗng hay gộp các dấu phân cách lặp lại. Ngay cả đối với giao thức HTTP, các quy tắc chuẩn hóa bổ sung cũng rất hẹp và chỉ liên quan đến việc xử lý cổng mặc định hoặc thành phần đường dẫn trống hoàn toàn (ví dụ: chuyển đường dẫn trống thành /), chứ không đụng đến các đoạn rỗng ở giữa.

Hệ quả thực tế: Một ví dụ về Git

Để thấy rõ sự nguy hiểm của việc tự động gộp dấu gạch chép, hãy xem xét một ví dụ thực tế với Git. Hãy tưởng tượng bạn có một kho lưu trữ Git được cấu hình để yêu cầu dấu // ở cuối URL như một ký hiệu phân tách (sentinel) để phân biệt giữa đường dẫn nhóm và tham chiếu Git.

Nếu bạn cố gắng clone kho này mà không có dấu // cuối cùng:

$ git clone https://git.runxiyu.org/furweb.git/
fatal: repository 'https://git.runxiyu.org/furweb.git/' not found

Lệnh này sẽ thất bại vì máy chủ không tìm thấy tài nguyên tại đường dẫn đó.

Tuy nhiên, với đường dẫn đúng chuẩn có chứa //:

$ git clone https://git.runxiyu.org/furweb.git//
Cloning into 'furweb'...
Receiving objects: 100% (2005/2005), done.

Lệnh này thành công. Rõ ràng, https://git.runxiyu.org/furweb.git/https://git.runxiyu.org/furweb.git// là hai định danh hoàn toàn khác nhau và phục vụ hai nội dung khác nhau. Một bộ chuẩn hóa tự động gộp dấu gạch chép sẽ làm hỏng chức năng này.

Tại sao lại muốn sử dụng dấu //?

Việc sử dụng dấu gạch chép kép rất hữu ích khi bạn cần một dấu phân cách rõ ràng giữa các phần khác nhau của một đường dẫn. Ví dụ, trong các URL nhúng các hệ thống phân cấp tùy ý (như đường dẫn nhóm và Git refs), việc có một ký hiệu phân tách cụ thể giúp tránh sự mơ hồ về nơi kết thúc phần này và bắt đầu phần khác.

Ví dụ:

  • https://example.com/group//repos/project/tree/HEAD//.editorconfig

Ở đây, dấu // đầu tiên có thể phân tách tên nhóm và đường dẫn kho, trong khi dấu // thứ hai phân tách tham chiếu (ref) và đường dẫn tệp. Nếu gộp tất cả thành /, cấu trúc logic này sẽ bị phá vỡ.

Các triển khai sai lầm cần lưu ý

Một số công nghệ phổ biến hiện nay vẫn thực hiện việc gộp dấu gạch chép một cách sai lầm, bao gồm:

  • nginx: Với tùy chọn merge_slashes (được bật theo mặc định), nginx sẽ gộp nhiều dấu gạch chép liên tiếp thành một.
  • Go: Gói net/http.ServeMux và hàm path.Clean trong ngôn ngữ lập trình Go cũng thực hiện hành vi này.

Các nhà phát triển cần cảnh giác với các hành vi mặc định này khi thiết kế hệ thống phụ thuộc vào tính chính xác tuyệt đối của cấu trúc URL.

Kết luận

Việc gộp dấu // thành / trong đường dẫn URL không phải là chuẩn hóa đúng chuẩn. Nó làm thay đổi định danh tài nguyên và có thể gây ra lỗi logic hoặc bảo mật. Các tiêu chuẩn RFC 3986 và RFC 9110 đều bảo toàn tính toàn vẹn của các đoạn đường dẫn rỗng. Trừ khi bạn kiểm soát hoàn toàn máy chủ và quy định rõ ràng sự tương đương, hãy luôn đối xử với // như một phần hợp lệ và có ý nghĩa của URL.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗