How to URL-Encode Torrent Info Hashes for Trackers

When a BitTorrent client communicates with an HTTP-based tracker via a GET request, it must send the torrent's unique info hash as a parameter in the query string. Because the info hash is a raw sequence of binary bytes rather than a plain text string, it contains arbitrary byte values that are invalid or reserved in standard URLs. To transmit this data correctly without corruption, clients convert the raw binary hash into an ASCII-compatible format using standard percent-encoding (URL-encoding) rules.

The Raw Binary Hash vs. Hex Strings

A common mistake in BitTorrent development is attempting to URL-encode the 40-character hexadecimal representation of a hash. BitTorrent trackers do not expect a hexadecimal string; they require the raw binary hash:

  • BitTorrent v1 (SHA-1): 20 raw bytes.
  • BitTorrent v2 (SHA-256): 32 raw bytes.

When communicating with the tracker via the ?info_hash= query parameter, the client must encode the 20 or 32 individual bytes directly, not the human-readable 40 or 64 hex characters.

Standard URL-Encoding Rules

The BitTorrent protocol follows standard RFC 3986 URI encoding rules for the raw bytes:

  1. Unreserved Characters: Bytes that correspond to the ASCII values of alphanumeric characters (a-z, A-Z, 0-9) and the four unreserved symbols (-, _, ., ~) may be transmitted directly as literal characters.
  2. Reserved and Non-Printable Bytes: Any byte value that falls outside the unreserved character set—including control characters, whitespace, high-ASCII bytes (0x80 through 0xFF), and URL-reserved delimiters (such as &, =, ?, /)—must be encoded as % followed by two hexadecimal digits (e.g., 0x1F becomes %1F).

Step-by-Step Encoding Process

To illustrate the encoding process:

  1. Obtain the 20-byte hash: Suppose the first four bytes of a SHA-1 hash in hexadecimal are 0x41, 0x0A, 0x2E, and 0xFF.
  2. Evaluate byte by byte:
    • 0x41 corresponds to ASCII character A (unreserved) \(\rightarrow\) encoded as A (or %41).
    • 0x0A corresponds to a non-printable Line Feed \(\rightarrow\) encoded as %0A.
    • 0x2E corresponds to ASCII character . (unreserved) \(\rightarrow\) encoded as ..
    • 0xFF is outside standard ASCII \(\rightarrow\) encoded as %FF.
  3. Assemble the result: The four-byte segment becomes A%0A.%FF.

The complete URL-encoded info hash string typically ends up between 20 and 60 characters in length, depending on how many bytes map to unreserved ASCII characters versus percent-encoded sequences.

Important Implementation Considerations

  • Literal %20 vs. +: Space bytes (0x20) must always be encoded as %20. Unlike standard HTML form data (application/x-www-form-urlencoded), using + to represent 0x20 can cause trackers to decode the character as a literal plus sign (0x2B), corrupting the hash.
  • Hex Case Sensitivity: While hexadecimal digits in percent-encoding (e.g., %0a vs %0A) are technically case-insensitive per RFC 3986, uppercase hexadecimal letters are standard and expected by most BitTorrent tracker implementations.
  • Double Encoding Pitfalls: Standard high-level HTTP client libraries often assume query parameters are UTF-8 text and may attempt to encode or re-encode strings automatically. To avoid corrupted hashes, developers must ensure the HTTP client treats the query parameters as raw binary data or pass the pre-encoded ASCII string directly without secondary transformation.