URL encoding atau percent-encoding adalah cara menulis karakter yang tidak boleh atau berbahaya muncul langsung di URL dengan format % diikuti dua digit heksadesimal. Spasi menjadi %20, tanda & menjadi %26, dan huruf é menjadi %C3%A9 (dua byte UTF-8). Tujuannya agar browser dan server membaca URL persis seperti yang Anda maksud.
Artikel ini menjelaskan karakter mana yang wajib di-encode, perbedaan encodeURIComponent dan encodeURI di JavaScript, perbedaan urlencode dan rawurlencode di PHP, kenapa spasi kadang jadi + dan kadang %20, serta cara menghindari bug double-encoding yang sering muncul di aplikasi nyata. Untuk mencoba langsung, gunakan tool URL Encode/Decode.
Kenapa URL perlu di-encode?
URL punya tata bahasa sendiri. Karakter tertentu adalah "tanda baca" yang memisahkan bagian-bagian URL: / memisahkan segmen path, ? memulai query string, & memisahkan parameter, = memisahkan nama dan nilai, dan # memulai fragment. Bayangkan Anda ingin mengirim pencarian kopi & teh lewat URL:
https://contoh.com/cari?q=kopi & teh&hal=1
Server akan membacanya sebagai parameter q=kopi , lalu parameter aneh bernama teh, lalu hal=1. Nilai pencarian Anda terpotong. Versi yang benar adalah:
https://contoh.com/cari?q=kopi%20%26%20teh&hal=1
Sekarang %26 diperlakukan sebagai data, bukan pemisah. Selain itu, URL secara standar hanya boleh berisi sebagian karakter ASCII. Huruf beraksen, aksara non-Latin, dan emoji harus diubah dulu menjadi byte UTF-8, lalu setiap byte ditulis sebagai %XX.
Karakter reserved dan unreserved (RFC 3986)
Standar URI, RFC 3986, membagi karakter menjadi dua kelompok penting:
| Kelompok | Karakter | Perlakuan |
|---|---|---|
| Unreserved | A–Z a–z 0–9 - . _ ~ | Tidak pernah perlu di-encode |
| Reserved (gen-delims) | : / ? # [ ] @ | Punya makna struktural; encode jika dipakai sebagai data |
| Reserved (sub-delims) | ! $ & ' ( ) * + , ; = | Punya makna di bagian tertentu; encode jika dipakai sebagai data |
| Lainnya | spasi, " < > % { } |, non-ASCII | Selalu di-encode |
Aturan praktisnya: karakter reserved boleh tampil apa adanya jika fungsinya sebagai pemisah. Jika karakter yang sama adalah bagian dari data (misalnya tanda & di dalam nama produk), ia harus di-encode. Tanda % sendiri juga selalu di-encode menjadi %25, karena ia penanda awal percent-encoding.
Bagaimana karakter non-ASCII di-encode?
Karakter seperti é dikonversi ke UTF-8 terlebih dahulu. Huruf é di UTF-8 terdiri dari dua byte, 0xC3 0xA9, sehingga hasilnya %C3%A9. Karakter Jepang atau emoji bisa menghasilkan tiga sampai empat byte, jadi satu karakter bisa menjadi 9–12 karakter di URL. Inilah sebabnya URL berbahasa non-Latin terlihat sangat panjang saat disalin dari address bar.
JavaScript: encodeURIComponent vs encodeURI
JavaScript punya dua fungsi yang namanya mirip tetapi tujuannya berbeda. Salah memilih adalah sumber bug paling umum.
encodeURIComponent: untuk satu bagian data
Gunakan encodeURIComponent() untuk meng-encode satu nilai yang akan disisipkan ke URL, misalnya nilai parameter query atau satu segmen path. Fungsi ini meng-encode hampir semua karakter reserved, kecuali A–Z a–z 0–9 - _ . ! ~ * ' ( ).
const q = 'kopi & teh/susu?';
const url = 'https://contoh.com/cari?q=' + encodeURIComponent(q);
// https://contoh.com/cari?q=kopi%20%26%20teh%2Fsusu%3F
encodeURI: untuk URL utuh
encodeURI() dirancang untuk URL lengkap yang sudah benar strukturnya tetapi mungkin berisi spasi atau karakter non-ASCII. Fungsi ini tidak meng-encode ; / ? : @ & = + $ , #, karena karakter-karakter tersebut dianggap sebagai struktur URL.
encodeURI('https://contoh.com/produk baru?kat=é#info');
// https://contoh.com/produk%20baru?kat=%C3%A9#info
Artinya, jika Anda memakai encodeURI() untuk sebuah nilai parameter yang mengandung &, tanda tersebut lolos tanpa di-encode dan memecah query string Anda. Aturan mudahnya: hampir selalu gunakan encodeURIComponent() untuk data, dan jarang sekali butuh encodeURI().
Cara modern: URL dan URLSearchParams
Daripada menyusun string secara manual, gunakan API URL dan URLSearchParams. Keduanya meng-encode nilai secara otomatis:
const url = new URL('https://contoh.com/cari');
url.searchParams.set('q', 'kopi & teh');
url.searchParams.set('hal', 2);
console.log(url.toString());
// https://contoh.com/cari?q=kopi+%26+teh&hal=2
// Membaca kembali, otomatis di-decode
url.searchParams.get('q'); // "kopi & teh"
Perhatikan bahwa URLSearchParams menulis spasi sebagai +, bukan %20. Ini sah karena mengikuti format application/x-www-form-urlencoded, yang dibahas di bagian berikutnya.
Untuk decode, pasangannya adalah decodeURIComponent(). Fungsi ini akan melempar URIError jika menemukan urutan persen yang rusak, misalnya %E0%A4%A, jadi bungkus dengan try...catch saat memproses input yang tidak tepercaya.
PHP: urlencode vs rawurlencode
PHP juga punya dua fungsi, dan perbedaan utamanya ada pada cara menangani spasi dan tilde:
<?php
echo urlencode('a b~*'); // a+b%7E%2A
echo rawurlencode('a b~*'); // a%20b~%2A
rawurlencode()mengikuti RFC 3986: spasi menjadi%20dan~dibiarkan. Gunakan untuk segmen path URL.urlencode()mengikuti format form HTML: spasi menjadi+. Cocok untuk nilai query string gaya form.
Pasangan decode-nya adalah urldecode() (mengubah + menjadi spasi) dan rawurldecode() (membiarkan + apa adanya). Mencampur keduanya bisa membuat tanda plus asli dalam data, misalnya nomor telepon +62812..., berubah menjadi spasi.
Untuk menyusun query string dari array, gunakan http_build_query() yang sudah meng-encode setiap nilai:
<?php
$query = http_build_query(['q' => 'kopi & teh', 'hal' => 2]);
// q=kopi+%26+teh&hal=2
// Paksa spasi menjadi %20
$query = http_build_query(['q' => 'kopi teh'], '', '&', PHP_QUERY_RFC3986);
// q=kopi%20teh
Di Laravel, Anda jarang perlu memanggil fungsi ini langsung. Helper route('produk.show', ['slug' => $slug, 'q' => $q]) dan url() sudah meng-encode parameter. Begitu pula $request->query('q') sudah mengembalikan nilai yang ter-decode.
Berlangganan gratis. Artikel & tutorial coding terbaru langsung ke email kamu. Tanpa spam.
Tanda + vs %20: mana yang benar?
Keduanya benar, tetapi di konteks yang berbeda:
- Di path URL, spasi harus
%20. Tanda+di path adalah tanda plus literal. URL/file/laporan+akhir.pdfmerujuk ke file bernamalaporan+akhir.pdf. - Di query string yang diproses sebagai data form (
application/x-www-form-urlencoded),+berarti spasi. Hampir semua framework backend, termasuk PHP dan Laravel, mendekodenya begitu.
Masalah muncul ketika data Anda memang berisi tanda plus, misalnya nomor telepon internasional atau string Base64. Jika tanda + tidak di-encode menjadi %2B, server akan membacanya sebagai spasi. Contoh klasik: ?telp=+628123 sampai di PHP sebagai " 628123". Solusinya, selalu encode nilai parameter dengan encodeURIComponent() yang mengubah + menjadi %2B.
Bug double-encoding dan cara menghindarinya
Double-encoding terjadi ketika nilai yang sudah di-encode di-encode lagi. Tanda % berubah menjadi %25, sehingga %20 menjadi %2520. Di sisi server, hasil decode sekali hanyalah %20 mentah, bukan spasi.
const nama = 'Budi Santoso';
const sekali = encodeURIComponent(nama); // Budi%20Santoso
const dua = encodeURIComponent(sekali); // Budi%2520Santoso ← bug
Penyebab umumnya:
- Meng-encode nilai secara manual, lalu memasukkannya ke
URLSearchParamsatauhttp_build_query()yang meng-encode lagi. - Menyimpan URL yang sudah di-encode ke database, lalu meng-encode ulang saat menampilkan.
- Redirect berantai, di mana setiap tahap menambahkan URL tujuan sebagai parameter dan meng-encode ulang.
- Library HTTP (Axios, Guzzle, Laravel HTTP Client) yang menerima parameter dalam bentuk array sudah meng-encode, tetapi developer tetap meng-encode nilainya sendiri.
Prinsip pencegahannya: encode tepat satu kali, di tempat URL disusun. Simpan data mentah di variabel dan database, dan biarkan satu lapisan, sebaiknya API URL bawaan atau helper framework, yang melakukan encoding. Jika Anda menemukan %25 di log server, itu tanda kuat ada double-encoding.
Kebalikannya, double-decoding, juga berbahaya. Jika aplikasi men-decode input dua kali, penyerang bisa menyelundupkan %252e%252e%252f yang setelah dua kali decode menjadi ../, sehingga lolos dari filter path traversal. Decode sekali saja, lalu validasi hasilnya.
URL encoding bukan pengganti escaping lain
Percent-encoding hanya membuat data aman di dalam URL. Saat URL dicetak ke HTML, Anda tetap perlu HTML escaping (di Blade cukup {{ $url }}). Saat nilai masuk ke query SQL, gunakan prepared statement. Setiap konteks punya aturan escaping sendiri, dan satu tidak menggantikan yang lain. Pemahaman tentang bagian-bagian request HTTP juga membantu; lihat cara kerja HTTP dan HTTPS serta dasar REST API untuk konteks query string dan path parameter.
Kesalahan umum
- Memakai
encodeURI()untuk nilai parameter, sehingga&dan=tidak ter-encode. - Menyusun URL dengan penggabungan string tanpa encoding sama sekali.
- Lupa bahwa
+di query string berarti spasi, sehingga nomor telepon atau Base64 rusak. - Meng-encode dua kali dan menghasilkan
%2520. - Memakai
urlencode()untuk segmen path di PHP, sehingga spasi menjadi+literal. - Men-decode input pengguna lebih dari sekali sebelum validasi.
Ringkasan
- Karakter unreserved (
A–Z a–z 0–9 - . _ ~) aman; selebihnya di-encode jika dipakai sebagai data. - JavaScript:
encodeURIComponent()untuk nilai,URL/URLSearchParamsuntuk menyusun URL. - PHP:
rawurlencode()untuk path,urlencode()atauhttp_build_query()untuk query. %20selalu aman untuk spasi;+hanya berarti spasi di query gaya form.- Encode tepat sekali, decode tepat sekali.
FAQ
Apa beda URL encoding dan percent-encoding?
Keduanya sama. "Percent-encoding" adalah nama resmi di RFC 3986, karena setiap byte ditulis dengan tanda % dan dua digit heksadesimal. "URL encoding" adalah sebutan sehari-harinya.
Kapan saya harus memakai encodeURI?
Hampir tidak pernah untuk data. encodeURI() hanya cocok untuk merapikan URL utuh yang strukturnya sudah benar tetapi berisi spasi atau karakter non-ASCII. Untuk nilai parameter atau segmen path, gunakan encodeURIComponent().
Kenapa tanda plus di parameter saya berubah jadi spasi?
Karena di query string gaya form, + diartikan sebagai spasi. Encode nilai dengan encodeURIComponent() agar + menjadi %2B, atau gunakan URLSearchParams yang menangani ini otomatis.
Apa itu %2520 di URL saya?
Itu tanda double-encoding: %20 (spasi) di-encode lagi sehingga % menjadi %25. Cari bagian kode yang meng-encode nilai yang sudah ter-encode, dan pastikan encoding hanya terjadi sekali.
Apakah huruf dan angka perlu di-encode?
Tidak. Huruf A–Z, a–z, angka 0–9, serta -, ., _, dan ~ adalah karakter unreserved yang selalu aman dipakai apa adanya.