URL encode tiếng Việt | Space, +, %20 và bẫy double-encode khi copy từ trình duyệt

(Cập nhật: 19 tháng 7, 2026 ) URL encode decode percent-encoding tiếng Việt query string API
Kết luận

Query tiếng Việt phải percent-encode đúng một lần. Space → ưu tiên %20; + chỉ đáng tin trong form-urlencoded. Path segmentquery value encode riêng (encodeURIComponent); không encode cả URL. Copy từ thanh địa chỉ Chrome thường đã encode — encode thêm = double-encode (%25). Kiểm nhanh: URL Encode/Decode.

API search q=Hà Nội trên staging trả 0 kết quả. Log gateway hiện q=H%25C3%25A0%2520N%25E1%25BB%2599i. Đó không phải bug Elasticsearch — pipeline đã encode hai lần. Freelancer copy URL từ thanh địa chỉ (đã %C3%A0) rồi encodeURIComponent lại trước khi gắn vào fetch.

Bài này dành dev / QA Việt Nam xây search, filter, deeplink có tiếng Việt, khoảng trắng, dấu + — và cần biết path khác query thế nào.

Bài viết này giúp bạn

  • Hiểu percent-encoding với chữ Việt (UTF-8 → %XX)
  • Phân biệt space / + / %20
  • Biết khi encode path vs query
  • Nhận và sửa double-encode
  • Dùng URL Encode Kawa để soi chuỗi trước khi blame backend

Percent-encoding là gì (ngắn, đủ dùng)

URL “an toàn” dùng tập ký tự hạn chế. Chữ có dấu tiếng Việt nằm ngoài tập đó → chuyển UTF-8 bytes rồi viết % + hex:

ChuỗiUTF-8 (hex)Trong URL
áC3 A1%C3%A1
E1 BB 87%E1%BB%87
space20%20 (hoặc + trong form)

Ví dụ:

Hà Nội  →  H%C3%A0%20N%E1%BB%99i

Thử encode/decode nhanh trên công cụ URL khi log chỉ toàn %E1%BB....


Space, +, %20 — đừng đoán

Ba cách biểu diễn khoảng trắng
Dạng Ngữ cảnh Lưu ý
%20 RFC 3986 percent-encoding — path và query Lựa chọn an toàn nhất khi tự xây URL
+ application/x-www-form-urlencoded (form HTML cổ) Nhiều server query hiểu + = space; path thì + thường là dấu cộng thật
space thô URL bar đôi khi hiển thị đẹp Copy/share dễ gãy; HTTP wire gần như luôn encode

Thực tế VN:

  • Search áo sơ mi → query nên là q=%C3%A1o%20s%C6%A1%20mi
  • SKU có dấu + (ví dụ tee+black) trong path: encode + thành %2B nếu muốn literal cộng — kẻo một số parser query hiểu nhầm

Khi decode: %20 và (trong form context) + đều có thể thành space. Khi encode lại, đừng biến space thành + rồi encode %2B — dễ loạn.


Path vs query — encode khác nhau

https://shop.vn/danh-muc/đồ-nam?q=áo khoác&sort=price
         \_______path________/   \_____query______/

Path segments

  • Encode từng segment sau khi split /
  • Giữ / phân tách — không encodeURIComponent cả path
  • Non-ASCII, space, ?, # trong tên category → phải encode
const segment = encodeURIComponent('đồ nam'); // %C4%91%E1%BB%93%20nam
const path = `/danh-muc/${segment}`;

Query values

  • Encode từng value (và key nếu key có ký tự lạ)
  • Giữ ? & = làm cú pháp
const q = encodeURIComponent('áo khoác');
const url = `https://shop.vn/search?q=${q}&sort=price`;

Sai phổ biến

// SAI — encode cả URL → phá : / ?
encodeURIComponent('https://shop.vn/search?q=áo');

// SAI — encodeURI giữ ? & nhưng không encode đủ value có & trong text
encodeURI('https://shop.vn/search?q=a&b'); // &b thành param mới!
encodeURI vs encodeURIComponent
Hàm Giữ nguyên Dùng cho
encodeURI : / ? # [ ] @ ! $ & ' ( ) * + , ; = URL gần đủ; ít khi đủ an toàn cho value tự do
encodeURIComponent Ít hơn — encode cả & = ? / Từng query value / path segment

Double-encode — bẫy copy từ trình duyệt

Chuỗi lành

Hà Nội → H%C3%A0%20N%E1%BB%99i

Encode lần 2

%%25:

H%25C3%25A0%2520N%25E1%25BB%2599i

Backend decode một lần ra H%C3%A0%20N%E1%BB%99i (vẫn “mã”) — search không khớp “Hà Nội”.

Vì sao hay xảy ra ở VN workflow

  1. QA copy URL từ Chrome (thanh địa chỉ đã hiện dạng encode hoặc mix)
  2. Paste vào sheet / Zalo / ticket
  3. Dev encodeURIComponent(copied) trước khi gọi API
  4. Hoặc Nginx / API gateway encode thêm khi proxy

Cách phát hiện

  1. Decode một lần trên URL Decode
  2. Nếu vẫn thấy %C3 / %E1 dày đặc → có thể cần decode thêm hoặc pipeline đang double
  3. Nếu ra chữ Việt rõ ràng → dừng; đừng encode lại trước khi gửi nếu bạn đang cầm sẵn dạng wire

Quy tắc copy

NguồnCoi nhưViệc làm
Input user / DB plaintextChưa encodeencodeURIComponent một lần khi gắn URL
Thanh địa chỉ / access logĐã encodeDecode để đọc; gửi lại nguyên hoặc decode→encode sạch một lần
Document khách paste “đẹp”Không chắcDecode thử; nếu fail hoặc vẫn % thì soi pipeline

Case study

1. Filter tỉnh “Hồ Chí Minh”

Frontend:

params.set('city', 'Hồ Chí Minh'); // URLSearchParams encode giúp

URLSearchParams thường encode đúng một lần. Nguy hiểm khi bạn vừa params.set vừa encode tay value trước đó → double.

Custom scheme vẫn nên encode segment. Một số WebView decode khác browser — test bằng cách decode lại trên tool trước khi ship.

3. Sign HMAC trên query

Ký trên chuỗi đã encode đúng chuẩn mà server mong đợi. Nếu client ký plaintext nhưng server verify trên bản encode (hoặc ngược lại), chữ ký lệch — trông như “auth bug” nhưng là encode mismatch.


  • Value tiếng Việt chỉ encode một lần
  • Không encodeURIComponent cả URL
  • Space dùng %20 (trừ khi API bắt form + và đã document)
  • + literal trong SKU → %2B nếu cần
  • Copy từ browser → kiểm tra double (%25)
  • So raw trên URL Encode/Decode với kỳ vọng plaintext

Tóm tắt

  1. Tiếng Việt = UTF-8 percent-encoding.
  2. Query value / path segment encode riêng; giữ cấu trúc URL.
  3. %20 > đoán +, trừ form-urlencoded rõ ràng.
  4. Copy từ browser thường đã encode — encode thêm là double.

Khi log chỉ toàn %, đừng đọc bằng mắt: decode một lần trên tool local, rồi quyết định bước tiếp.

Liên kết liên quan