Advanced URL Techniques
Beyond basic URL encoding, advanced topics like internationalized domain names (IDNs), Internationalized Resource Identifiers (IRIs), and Punycode are essential for global web applications. This guide covers these topics and more.
Internationalized Domain Names (IDN)
Domain names can contain non-ASCII characters using Punycode encoding. This allows domain names in any language.
#### How IDN Works
- User types: https://example.中国
- Browser converts to Punycode: https://example.xn--fiqs8s
- DNS resolves the Punycode version
- Browser displays the Unicode version to the user
// Converting Unicode domain to Punycode
function toPunycode(domain) {
// Using the URL API
const url = new URL('https://' + domain);
return url.hostname;
}
// Example
console.log(toPunycode('example.中国')); // example.xn--fiqs8s
console.log(toPunycode('münchen.de')); // xn--mnchen-3ya.de
#### Common IDN Examples
| Unicode Domain | Punycode | Language |
| example.中国 | example.xn--fiqs8s | Chinese |
| münchen.de | xn--mnchen-3ya.de | German |
| café.fr | xn--caf-dma.fr | French |
| пример.рф | xn--e1afmkfd.xn--p1ai | Russian |
| 例え.テスト | xn--r8jz45g.xn--zckzah | Japanese |
IRI vs URI
IRI (Internationalized Resource Identifier) extends URI to allow non-ASCII characters:
| Feature | URI | IRI |
| Character set | ASCII only | Unicode |
| Domain names | Punycode | Unicode or Punycode |
| Path | Percent-encoded | Unicode (converted before transmission) |
| Query string | Percent-encoded | Unicode (converted before transmission) |
// IRI (what users see)
const iri = 'https://example.com/路径?查询=值';
// URI (what gets transmitted)
const uri = encodeURI(iri);
// https://example.com/%E8%B7%AF%E5%BE%84?%E6%9F%A5%E8%AF%A2=%E5%80%BC
URL Parsing and Manipulation
#### The URL API
Modern browsers provide the URL API for parsing and manipulating URLs:
const url = new URL('https://user:pass@example.com:8080/path/to/page?q=hello&lang=en#section1');
console.log({
protocol: url.protocol, // https:
username: url.username, // user
password: url.password, // pass
hostname: url.hostname, // example.com
port: url.port, // 8080
pathname: url.pathname, // /path/to/page
search: url.search, // ?q=hello&lang=en
hash: url.hash, // #section1
origin: url.origin, // https://example.com:8080
});
// Modifying URL parts
url.pathname = '/new/path';
url.searchParams.set('q', 'updated search');
url.hash = '#new-section';
#### URL Pattern Matching
// Using URLPattern API (modern browsers)
const pattern = new URLPattern({
protocol: 'https',
hostname: '*.example.com',
pathname: '/users/:id',
});
const match = pattern.exec('https://shop.example.com/users/123');
// { pathname: { id: '123' }, ... }
Advanced Query String Handling
#### Nested Objects
// Some APIs support nested query parameters
const params = new URLSearchParams();
params.set('user[name]', 'John');
params.set('user[age]', '30');
params.set('items[]', 'apple');
params.set('items[]', 'banana');
// user%5Bname%5D=John&user%5Bage%5D=30&items%5B%5D=apple&items%5B%5D=banana
#### Using URLSearchParams for Complex Operations
const params = new URLSearchParams('a=1&b=2&c=3');
// Iterate over parameters
for (const [key, value] of params) {
console.log(${key} = ${value});
}
// Get all values for a key
params.append('tag', 'js');
params.append('tag', 'web');
params.getAll('tag'); // ['js', 'web']
// Delete and has
params.delete('b');
params.has('b'); // false
URL Security Best Practices
#### 1. Prevent SSRF (Server-Side Request Forgery)
async function safeFetch(targetUrl) {
const url = new URL(targetUrl);
// Only allow HTTP(S)
if (!['http:', 'https:'].includes(url.protocol)) {
throw new Error('Invalid protocol');
}
// Block internal IPs
const hostname = url.hostname;
if (isInternalIp(hostname)) {
throw new Error('Internal IPs blocked');
}
// Resolve DNS and check the actual IP
const ips = await dns.resolve4(hostname);
for (const ip of ips) {
if (isInternalIp(ip)) {
throw new Error('DNS rebinding detected');
}
}
return fetch(url.toString());
}
#### 2. Prevent Open Redirects
function safeRedirect(target) {
try {
const url = new URL(target, window.location.origin);
// Only allow same-origin
if (url.origin !== window.location.origin) {
return '/error?code=invalid_redirect';
}
// Block javascript: and data: schemes
if (!['http:', 'https:'].includes(url.protocol)) {
return '/error?code=invalid_protocol';
}
return url.toString();
} catch {
return '/';
}
}
#### 3. URL Sanitization
function sanitizeUrl(input) {
try {
const url = new URL(input);
// Remove credentials
url.username = '';
url.password = '';
// Normalize
url.protocol = url.protocol.toLowerCase();
url.hostname = url.hostname.toLowerCase();
// Remove default ports
if ((url.protocol === 'https:' && url.port === '443') ||
(url.protocol === 'http:' && url.port === '80')) {
url.port = '';
}
return url.toString();
} catch {
return null;
}
}
URL Building for Different Frameworks
#### React/Next.js
import { useSearchParams, useRouter } from 'next/navigation';
function SearchComponent() {
const searchParams = useSearchParams();
const router = useRouter();
const updateSearch = (key, value) => {
const params = new URLSearchParams(searchParams);
params.set(key, value);
router.push('?/' + params.toString());
};
}
#### Express.js
app.get('/search', (req, res) => {
const query = req.query.q; // Already decoded
const page = parseInt(req.query.page) || 1;
// Express automatically decodes URL parameters
});
Using Our Tool
Our URL Encoder handles advanced encoding needs:
- Standard URL encoding and decoding
- Unicode and international character support
- Both component and full-URL encoding
- Local processing — no data sent to servers
- Instant results with copy-to-clipboard
Conclusion
Advanced URL techniques — IDNs, Punycode, IRIs, URL API, security hardening — are essential for building robust, international web applications. By understanding these concepts, you can handle URLs correctly across all languages and platforms. Use our URL Encoder for your encoding needs, and implement the security practices outlined here to protect your applications.