JavaScript URL エンコーディング完全ガイド
無料の URLエンコーダー をブラウザで直接使用 — インストール不要。
URLエンコーダー をオンラインで試す →検索 URL を構築したり、リダイレクトパスをクエリパラメータとして渡したり、 OAuth 認可リクエストを構築したりする際、 &、 =、スペースなどの特殊文字は、 URL エンコードしないと URL を静かに破損させます。 JavaScript には 3 つの組み込みアプローチがあります—— encodeURIComponent()、 encodeURI()、および URLSearchParams——それぞれ異なるユースケース向けに設計されており、 間違ったものを選ぶことが、コードレビューで私が出会ったほとんどのエンコーディングバグの根本原因です。 コードを書かずに素早く一度だけエンコードするには、 ToolDeck の URL Encoder がブラウザで即座に処理します。このガイドでは 3 つのアプローチすべてを詳しく解説します (JavaScript ES2015+ / Node.js 10+):それぞれをいつ使うか、スペースや予約文字の扱いの違い、 実際の Fetch と API パターン、CLI の使用方法、および最も微妙な本番バグを引き起こす 4 つの間違いについて説明します。
- ✓encodeURIComponent() は個々のパラメータ値をエンコードするための正しい選択です。A–Z、a–z、0–9、および - _ . ! ~ * ' ( ) を除くすべての文字をエンコードします
- ✓encodeURI() は構造文字(/ ? # & = :)を保持しながら完全な URL をエンコードします。個々の値には絶対に使用しないでください
- ✓URLSearchParams は application/x-www-form-urlencoded 形式を使用してキーと値のペアを自動エンコードし、スペースは %20 ではなく + になります
- ✓ダブルエンコーディングは最も一般的な本番バグです。encodeURIComponent(encodeURIComponent(value)) は %20 を %2520 に変えてしまいます
- ✓qs ライブラリはクエリ文字列内のネストされたオブジェクトと配列をネイティブに処理します。標準ライブラリの URLSearchParams はこれをサポートしていません
URL エンコーディングとは?
URL エンコーディング(正式にはパーセントエンコーディングと呼ばれ、 RFC 3986 で定義されています)は、URL で許可されていないか特別な意味を持つ文字を 安全な表現に変換します。 各安全でないバイトは、パーセント記号と 2 桁の16進数——その文字の ASCII コード——に置き換えられます。 スペースは %20 に、アンパサンドは %26 に、スラッシュは %2F になります。
常に安全でエンコードされない文字は非予約文字と呼ばれます: 文字 A–Z と a–z、数字 0–9、および 4 つの記号 - _ . ~。 それ以外はすべて、データとして使用する場合にエンコードする必要があるか、 URL 自体の構造的な役割を持ちます(たとえば / はパスセグメントを区切り、&はクエリパラメータを区切ります)。 実際の結果として、クエリパラメータに「無線キーボード & マウス」のような製品名が含まれる場合、 生のまま渡すと URL 構造が壊れます。
https://shop.example.com/search?q=Wireless Keyboard & Mouse&category=peripherals
https://shop.example.com/search?q=Wireless%20Keyboard%20%26%20Mouse&category=peripherals
encodeURIComponent() — クエリパラメータに適した関数
encodeURIComponent() は JavaScript における URL エンコーディングの主力関数です。 非予約セット(A–Z、a–z、0–9、- _ . ! ~ * ' ( ))を除くすべての文字をエンコードします。 重要なのは、URL で構造的な意味を持つすべての文字をエンコードすることです——&、 =、?、 #、/—— これにより、パラメータの値での使用が安全になります。 インポートは不要で、すべての JavaScript 環境でグローバル関数として利用できます。
最小限の動作例
// Encode a search query parameter that contains special characters
const searchQuery = 'Wireless Keyboard & Mouse'
const filterStatus = 'in-stock'
const maxPrice = '149.99'
const searchUrl = `https://shop.example.com/products?` +
`q=${encodeURIComponent(searchQuery)}` +
`&status=${encodeURIComponent(filterStatus)}` +
`&maxPrice=${encodeURIComponent(maxPrice)}`
console.log(searchUrl)
// https://shop.example.com/products?q=Wireless%20Keyboard%20%26%20Mouse&status=in-stock&maxPrice=149.99リダイレクト URL をクエリパラメータとしてエンコード
// The redirect destination is itself a URL — it must be fully encoded
// or the outer URL parser will misinterpret its ? and & as its own
const redirectAfterLogin = 'https://dashboard.internal/reports?view=weekly&team=platform'
const loginUrl = `https://auth.company.com/login?next=${encodeURIComponent(redirectAfterLogin)}`
console.log(loginUrl)
// https://auth.company.com/login?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform
// Decoding on the receiving end
const params = new URLSearchParams(window.location.search)
const next = params.get('next') // Automatically decoded
const nextUrl = new URL(next!) // Safe to parse
console.log(nextUrl.hostname) // dashboard.internal非 ASCII および Unicode 文字のエンコード
// encodeURIComponent handles Unicode natively in all modern environments
// Each UTF-8 byte of the character is percent-encoded
const customerName = '田中太郎'
const productTitle = '東京 wireless adapter'
const reviewText = '良い製品 — 完璧に動作する'
console.log(encodeURIComponent(customerName))
// %E7%94%B0%E4%B8%AD%E5%A4%AA%E9%83%8E
console.log(encodeURIComponent(productTitle))
// %E6%9D%B1%E4%BA%AC%20wireless%20adapter
console.log(encodeURIComponent(reviewText))
// %E8%89%AF%E3%81%84%E8%A3%BD%E5%93%81%20%E2%80%94%20%E5%AE%8C%E7%92%A7%E3%81%AB%E5%8B%95%E4%BD%9C%E3%81%99%E3%82%8B
// Decoding back
console.log(decodeURIComponent('%E7%94%B0%E4%B8%AD%E5%A4%AA%E9%83%8E'))
// 田中太郎encodeURIComponent() は JavaScript エンジンの内部 UTF-16 文字列エンコーディングを使用し、 その後、文字の各 UTF-8 バイトを個別にエンコードします。 ü(U+00FC)のような文字は %C3%BC にエンコードされます。なぜなら、その UTF-8 表現は 2 バイト(0xC3 と 0xBC)だからです。 これは正しく、URI 標準に従っています——サーバーは UTF-8 バイトシーケンスを元のコードポイントにデコードすることが期待されています。JavaScript URL エンコーディング関数——文字リファレンス
3 つのネイティブエンコーディングアプローチは、エンコードする文字が異なります。 以下の表は、最も問題になりやすい文字の出力を示しています:
| 文字 | URL での役割 | encodeURIComponent() | encodeURI() | URLSearchParams |
|---|---|---|---|---|
| Space | 単語区切り | %20 | %20 | + |
| & | パラメータ区切り | %26 | kept | %26 |
| = | キー=値 | %3D | kept | %3D |
| + | エンコードされたスペース(フォーム) | %2B | %2B | %2B |
| ? | クエリ開始 | %3F | kept | %3F |
| # | フラグメント | %23 | kept | %23 |
| / | パス区切り | %2F | kept | %2F |
| : | スキーム / ポート | %3A | kept | %3A |
| @ | 認証区切り | %40 | kept | %40 |
| % | パーセントリテラル | %25 | %25 | %25 |
| ~ | 非予約 | kept | kept | kept |
重要な列は、& と = に対する encodeURIComponent() と encodeURI() の違いです:encodeURI() はそれらをそのままにします。 これは完全な URL をエンコードする場合は正しいですが、 これらの文字を含む値をエンコードする場合は致命的です。
encodeURI() — URL 構造を維持する場合
encodeURI() は完全な URL をエンコードするために設計されています—— URI の有効な構造的部分であるすべての文字を保持します:スキーム(https://)、 ホスト、パス区切り文字、クエリ区切り文字、フラグメント識別子。 パスセグメントにスペースや非 ASCII 文字を含む可能性がある URL を受け取り、 その構造をそのままにしておく必要がある場合に使用します。
ユーザー提供の URL をサニタイズ
// A URL pasted from a document with spaces in the path and non-ASCII chars const rawUrl = 'https://cdn.example.com/assets/product images/München chair.png' const safeUrl = encodeURI(rawUrl) console.log(safeUrl) // https://cdn.example.com/assets/product%20images/M%C3%BCnchen%20chair.png // encodeURIComponent would break it — it encodes the :// and all / characters const broken = encodeURIComponent(rawUrl) console.log(broken) // https%3A%2F%2Fcdn.example.com%2Fassets%2Fproduct%20images%2FM%C3%BCnchen%20chair.png // ↑ Not a valid URL — the scheme and slashes are destroyed
URL コンストラクタは通常 encodeURI() よりも良い選択です——URL を正規化し、 構造を検証し、各コンポーネントにアクセスするためのクリーンな API を提供します。encodeURI() は、文字列結果が必要で、 入力が構造的に有効な URL であることがすでにわかっている場合のために残しておいてください。URLSearchParams — クエリ文字列のモダンなアプローチ
URLSearchParams は、モダンな JavaScript で クエリ文字列を構築・解析する慣用的な方法です。 すべてのモダンブラウザと Node.js 10+ でグローバルに利用でき、 エンコーディングを自動的に処理します——プレーンなキーと値のペアで作業するだけで、 正しい出力が生成されます。重要な詳細として: application/x-www-form-urlencoded 仕様に従い、 スペースを %20 ではなく + としてエンコードします。 これは正しく広くサポートされていますが、 サーバーが特定の形式を期待する場合は注意してください。
検索リクエスト URL の構築
// Building a search API URL with multiple parameters
const filters = {
query: 'standing desk',
category: 'office-furniture',
minPrice: '200',
maxPrice: '800',
inStock: 'true',
sortBy: 'price_asc',
}
const params = new URLSearchParams(filters)
const apiUrl = `https://api.example.com/v2/products?${params}`
console.log(apiUrl)
// https://api.example.com/v2/products?query=standing+desk&category=office-furniture&minPrice=200&maxPrice=800&inStock=true&sortBy=price_asc
// Appending additional params after construction
params.append('page', '2')
params.append('tag', 'ergonomic & adjustable')
console.log(params.toString())
// query=standing+desk&...&tag=ergonomic+%26+adjustable受信クエリ文字列の解析
// Both browser (window.location.search) and Node.js (req.url) scenarios
function parseWebhookCallbackUrl(rawSearch: string) {
const params = new URLSearchParams(rawSearch)
return {
eventId: params.get('event_id'), // null if missing
timestamp: Number(params.get('ts')),
signature: params.get('sig'),
redirectUrl: params.get('redirect'), // Automatically decoded
tags: params.getAll('tag'), // Handles repeated keys
}
}
const callbackQuery = '?event_id=evt_9c2f&ts=1717200000&sig=sha256%3Dabc123&redirect=https%3A%2F%2Fdashboard.internal%2F&tag=payment&tag=webhook'
const parsed = parseWebhookCallbackUrl(callbackQuery)
console.log(parsed.redirectUrl) // https://dashboard.internal/
console.log(parsed.tags) // ['payment', 'webhook']JavaScript Fetch リクエストでパラメータを URL エンコードする方法
本番コードで URL エンコーディングが最もよく登場するのは fetch() の呼び出し内です—— リクエスト URL の構築か、リクエストボディにフォームデータを送信する場合です。 それぞれのシナリオに適した正しいアプローチがあります。
GET リクエスト——クエリパラメータのエンコード
async function searchInventory(params: {
sku?: string
warehouse: string
minStock: number
updatedAfter?: Date
}): Promise<{ items: unknown[]; total: number }> {
const searchParams = new URLSearchParams()
if (params.sku) searchParams.set('sku', params.sku)
searchParams.set('warehouse', params.warehouse)
searchParams.set('min_stock', String(params.minStock))
if (params.updatedAfter) searchParams.set('updated_after', params.updatedAfter.toISOString())
const url = `https://inventory.internal/api/items?${searchParams}`
const res = await fetch(url, {
headers: {
'Authorization': `Bearer ${process.env.INVENTORY_API_KEY}`,
'Accept': 'application/json',
},
})
if (!res.ok) {
throw new Error(`Inventory API ${res.status}: ${await res.text()}`)
}
return res.json()
}
const results = await searchInventory({
warehouse: 'eu-west-1',
minStock: 10,
updatedAfter: new Date('2025-01-01'),
})
console.log(`Found ${results.total} items`)POST リクエスト——フォームボディの URL エンコード
// application/x-www-form-urlencoded POST — used by OAuth token endpoints,
// legacy form-submission APIs, and some webhook providers
async function requestOAuthToken(
clientId: string,
clientSecret: string,
code: string,
redirectUri: string,
): Promise<{ access_token: string; expires_in: number }> {
const body = new URLSearchParams({
grant_type: 'authorization_code',
client_id: clientId,
client_secret: clientSecret,
code,
redirect_uri: redirectUri, // URLSearchParams encodes this automatically
})
const res = await fetch('https://oauth.provider.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
// body: body — also works directly in modern environments (Fetch spec accepts URLSearchParams)
})
if (!res.ok) {
const err = await res.json()
throw new Error(`OAuth error: ${err.error_description ?? err.error}`)
}
return res.json()
}スクリプトを設定せずにエンコードされた URL をテストまたはデバッグする必要がある場合は、 生の値を直接 URL Encoder に貼り付けてください——ブラウザとサーバーが見るものを正確に示しながら、即座にエンコードとデコードを行います。 OAuth リダイレクト URI、webhook コールバック URL、CDN 署名付きリクエストパラメータの検査に便利です。
Node.js とシェルによるコマンドライン URL エンコーディング
シェルスクリプト、CI パイプライン、または簡単なデバッグのために、 完全なスクリプトを書かずに動作するいくつかのコマンドラインアプローチがあります。
# ── Node.js one-liners — cross-platform (macOS, Linux, Windows) ────────
# encodeURIComponent — encode a single value
node -e "console.log(encodeURIComponent(process.argv[1]))" "Wireless Keyboard & Mouse"
# Wireless%20Keyboard%20%26%20Mouse
# Build a complete query string
node -e "console.log(new URLSearchParams(JSON.parse(process.argv[1])).toString())" '{"q":"standing desk","category":"office-furniture","page":"1"}'
# q=standing+desk&category=office-furniture&page=1
# Decode a percent-encoded string
node -e "console.log(decodeURIComponent(process.argv[1]))" "Wireless%20Keyboard%20%26%20Mouse"
# Wireless Keyboard & Mouse
# ── curl — automatic encoding ───────────────────────────────────────────
# curl --data-urlencode encodes values automatically in GET and POST
curl -G "https://api.example.com/search" --data-urlencode "q=Wireless Keyboard & Mouse" --data-urlencode "category=office furniture"
# ── Python one-liner (available on most systems) ─────────────────────────
python3 -c "from urllib.parse import quote; print(quote(input(), safe=''))"
# (type the string and press Enter)
# ── jq + bash: encode every value in a JSON object ───────────────────────
echo '{"q":"hello world","tag":"node & express"}' | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(new URLSearchParams(d).toString())"
# q=hello+world&tag=node+%26+express高性能な代替手段:qs
組み込みの URLSearchParams は、 ネストされたオブジェクトや配列をサポートしていません—— 多くの API やフレームワークが使用するクエリ文字列の形式です。 qs ライブラリ(週間 npm ダウンロード数 3000 万超)は、 実際に使用されているクエリ文字列パターンの全範囲を処理します: ブラケット表記によるネストされたオブジェクト(filters[status]=active)、 繰り返しキー、カスタムエンコーダー、設定可能な配列シリアライゼーション形式。
npm install qs # or pnpm add qs
import qs from 'qs'
// URLSearchParams cannot represent this structure natively
const reportFilters = {
dateRange: {
from: '2025-01-01',
to: '2025-03-31',
},
status: ['published', 'archived'],
author: { id: 'usr_4f2a9c1b', role: 'editor' },
workspace: 'ws-platform-eu',
}
// qs produces bracket-notation query strings used by Express, Rails, Django REST
const query = qs.stringify(reportFilters, { encode: true })
console.log(query)
// dateRange%5Bfrom%5D=2025-01-01&dateRange%5Bto%5D=2025-03-31&status%5B0%5D=published&status%5B1%5D=archived&author%5Bid%5D=usr_4f2a9c1b&author%5Brole%5D=editor&workspace=ws-platform-eu
// Human-readable (no encoding) — useful for debugging
console.log(qs.stringify(reportFilters, { encode: false }))
// dateRange[from]=2025-01-01&dateRange[to]=2025-03-31&status[0]=published&status[1]=archived...
// Parsing back
const parsed = qs.parse(query)
console.log(parsed.dateRange) // { from: '2025-01-01', to: '2025-03-31' }
console.log(parsed.status) // ['published', 'archived']フラットなキーと値のパラメータには、URLSearchParams が常に正しい選択です—— 組み込みで、オーバーヘッドがなく、普遍的にサポートされています。 ネストされた構造が必要な場合、繰り返しキー以外の配列シリアライゼーション形式、 またはブラケット表記クエリ文字列を期待するバックエンドフレームワークとの統合が必要な場合のみ、 qs を使用してください。
よくある間違い
本番コードベースでこれらの 4 つの間違いを繰り返し見てきました—— ほとんどは値に特殊文字が含まれる場合にのみ表面化するサイレント失敗であり、 まさにユニットテストをすり抜けて実際のユーザーデータでのみ現れるバグの典型です。
間違い 1 — クエリパラメータ値に encodeURI() を使用
問題: encodeURI() は &、 =、または + をエンコードしません。 値にこれらの文字が含まれると、クエリ文字列の構文として解釈され、 サイレントにパラメータを分割または上書きします。 修正: 個々の値には常に encodeURIComponent() を使用してください。
// ❌ encodeURI does not encode & and = in the value
// "plan=pro&promo=SAVE20" is treated as two separate params
const planName = 'Pro Plan (save=20% & free trial)'
const url = `/checkout?plan=${encodeURI(planName)}`
// /checkout?plan=Pro%20Plan%20(save=20%%20&%20free%20trial)
// ↑ & breaks the query string// ✅ encodeURIComponent encodes & and = safely
const planName = 'Pro Plan (save=20% & free trial)'
const url = `/checkout?plan=${encodeURIComponent(planName)}`
// /checkout?plan=Pro%20Plan%20(save%3D20%25%20%26%20free%20trial)
// ↑ = and & are both encoded間違い 2 — すでにエンコードされた文字列のダブルエンコーディング
問題: すでにパーセントエンコードされた値に対して encodeURIComponent() を呼び出すと、% が %25 になり、%20 が %2520 になります。 サーバーは一度デコードして、スペースではなくリテラルの %20 を受け取ります。 修正: 最初にデコードしてから再エンコードするか、値が二度エンコードされないようにしてください。
// ❌ encodedParam already contains %20 — encoding again produces %2520
const encodedParam = 'Berlin%20Office'
const url = `/api/locations/${encodeURIComponent(encodedParam)}`
// /api/locations/Berlin%2520Office
// Server sees: "Berlin%20Office" (a literal percent-twenty, not a space)// ✅ Decode first if the value may already be encoded
const maybeEncoded = 'Berlin%20Office'
const clean = decodeURIComponent(maybeEncoded) // 'Berlin Office'
const url = `/api/locations/${encodeURIComponent(clean)}`
// /api/locations/Berlin%20Office — correct間違い 3 — URLSearchParams と encodeURIComponent の + / %20 の違いを考慮しない
問題: URLSearchParams はスペースを +(application/x-www-form-urlencoded)としてエンコードし、encodeURIComponent() は %20 を使用します。 同じ URL 内で両方を混在させると——たとえば、URLSearchParams の出力に事前エンコードされた文字列を追加する場合—— 一部のパーサーを混乱させる不整合なエンコーディングが生成されます。 修正: 1 つのアプローチを選び、URL 構築関数全体で一貫して使用してください。
// ❌ Mixed: URLSearchParams uses + for spaces, but we're appending
// a manually-encoded segment that uses %20
const base = new URLSearchParams({ category: 'office furniture' })
const extra = `sort=${encodeURIComponent('price asc')}`
const url = `/api/products?${base}&${extra}`
// /api/products?category=office+furniture&sort=price%20asc
// ↑ two different space encodings in the same URL// ✅ Use URLSearchParams exclusively — consistent encoding throughout
const params = new URLSearchParams({
category: 'office furniture',
sort: 'price asc',
})
const url = `/api/products?${params}`
// /api/products?category=office+furniture&sort=price+asc間違い 4 — スラッシュを含むパスセグメントのエンコードを忘れる
問題: REST パスセグメントとして使用されるリソース識別子(ファイルパスの reports/2025/q1.pdf など)には、 サーバールーターがパス区切り文字として解釈する / 文字が含まれており、 存在しないエンドポイントにルーティングされます。 修正: スラッシュを含む可能性があるパスセグメントには常に encodeURIComponent() を使用してください。
// ❌ The file path contains / — the server receives a 3-segment path
// instead of a single resource ID
const filePath = 'reports/2025/q1-financials.pdf'
const url = `https://storage.example.com/objects/${filePath}`
// https://storage.example.com/objects/reports/2025/q1-financials.pdf
// → Server routes to /objects/:year/:filename — 404 or wrong resource// ✅ encodeURIComponent encodes / as %2F — single path segment
const filePath = 'reports/2025/q1-financials.pdf'
const url = `https://storage.example.com/objects/${encodeURIComponent(filePath)}`
// https://storage.example.com/objects/reports%2F2025%2Fq1-financials.pdf
// → Server receives the full file path as one resource identifierJavaScript URL エンコーディングメソッド——クイック比較
| メソッド | スペースのエンコード | & = ? のエンコード | # / : のエンコード | ユースケース | インストール |
|---|---|---|---|---|---|
| encodeURIComponent() | %20 | ✅ yes | ✅ yes | 個々のパラメータ値のエンコード | No |
| encodeURI() | %20 | ❌ no | ❌ no | 完全な URL 文字列のサニタイズ | No |
| URLSearchParams | + | ✅ yes | ✅ yes | クエリ文字列の構築と解析 | No |
| URL constructor | コンポーネントで自動 | auto | auto | 完全な URL の構築と正規化 | No |
| qs library | %20(設定可能) | ✅ yes | ✅ yes | クエリ文字列のネストされたオブジェクトと配列 | npm install qs |
ほとんどの場合、選択は 3 つのシナリオに絞られます。 構造化データから複数パラメータのクエリ文字列を構築する場合は URLSearchParams を使用してください—— 最も安全で可読性が高い選択肢です。 テンプレートリテラル URL で単一の値をエンコードする場合、パスセグメントの場合、 またはスペースに + ではなく %20 を期待するシステム(AWS S3 署名 URL など)の場合は、encodeURIComponent() を使用してください。 クエリ文字列が URLSearchParams でネイティブに表現できないネストされたオブジェクトや配列を持つ場合のみ、 qs を使用してください。
よくある質問
関連ツール
コードを書かずにワンクリックでエンコードまたはデコードするには、 文字列を直接 URL Encoder に貼り付けてください——ブラウザでパーセントエンコーディングとデコーディングを即座に処理し、 エンコードされた出力を fetch 呼び出しやターミナルにコピーする準備ができています。
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.