A file name such as annual report #1.pdf or café.pdf needs care when it appears in a URL path. Spaces, Unicode characters, and URL delimiters are represented differently in a URL than in a file name. This guide explains how to encode one file-name segment, avoid double-encoding, and check that a download works from browser to server.
Key Takeaways
- Encode a file name as a single path segment, not as an entire URL.
- UTF-8 characters are represented by percent-encoding their bytes:
ébecomes%C3%A9. - Use
%20for a space in a path. A+represents a space in some form-encoded query data, not in an ordinary path segment. - Encode characters such as
#and?when they are part of the file name; otherwise, they can be interpreted as URL delimiters. - Encoding makes a URL representation; it does not guarantee that the server, application, or filesystem will find or serve the file.
What percent-encoding does
A URL has structural characters that separate its parts. For example, / separates path segments, ? begins a query, and # begins a fragment. When data inside one component needs to include a character that has a structural meaning, percent-encoding represents it as % followed by two hexadecimal digits for each encoded byte.
For non-ASCII text, the characters are first encoded as UTF-8 bytes. For example, é is the two-byte UTF-8 sequence C3 A9, so it appears as %C3%A9 in a percent-encoded path. The WHATWG URL Standard describes URL parsing and serialization; RFC 3986 defines percent-encoding and reserved characters.
| File-name character | In a path segment | Why |
|---|---|---|
| Space | %20 | A literal space is not used directly in a serialized URL. |
é | %C3%A9 | UTF-8 bytes are percent-encoded. |
# | %23 | Otherwise it starts a URL fragment. |
? | %3F | Otherwise it starts a query. |
% | %25 | % introduces a percent-encoded byte. |
/ | Not reliably usable as part of one filename segment | It separates path segments, and servers may handle an encoded slash differently. |
For example, the file name café report #1.pdf can be represented as this path segment:
caf%C3%A9%20report%20%231.pdf
A complete example URL is:
https://example.com/downloads/caf%C3%A9%20report%20%231.pdf
The URL may be displayed with some characters decoded by a browser, while its serialized or transmitted form uses percent-encoding.
Encode a file-name segment, not the whole URL
Encoding an entire URL changes its structure. For example, encoding https://example.com/downloads/report.pdf as one value would also encode the colons and slashes that identify the scheme and path. The result is data, not a normal navigable URL.
Instead, preserve the URL structure and encode only the file name. In JavaScript, encodeURIComponent is a useful starting point for one segment. It encodes UTF-8 characters and characters such as spaces, #, ?, and /. It leaves a few punctuation characters unescaped, so the helper below also escapes !, ', (, ), and * for a stricter segment representation.
function encodePathSegment(value) {
return encodeURIComponent(value).replace(/[!'()*]/g, (character) => {
return `%${character.charCodeAt(0).toString(16).toUpperCase()}`;
});
}
const fileName = "café report #1.pdf";
const path = `/downloads/${encodePathSegment(fileName)}`;
console.log(path);
// /downloads/caf%C3%A9%20report%20%231.pdf
Use the resulting path with your site’s origin or link-building code. Do not feed the complete URL into encodePathSegment; it is intended for one raw file-name value. JavaScript's encodeURIComponent can throw a URIError for malformed Unicode input, such as an unpaired surrogate, so validate or correct untrusted text before using it.
If a file name contains /, do not assume encoding it as %2F will work. Some servers, proxies, and routing layers reject encoded slashes or interpret them as path separators after decoding. If slash-like characters must be supported in a displayed name, use a route design that does not put the raw name in a path segment—for example, an opaque file ID—and let the application provide the intended download name.
Avoid encoding a URL twice
Percent-encoding should normally be applied once to the original, unencoded file-name value. If you encode the already encoded text caf%C3%A9.pdf again, % becomes %25, producing caf%25C3%25A9.pdf. The server may then see the literal text %C3%A9 instead of café.
A practical rule is to keep two values distinct:
- Raw name:
café report #1.pdf - Encoded path segment:
caf%C3%A9%20report%20%231.pdf
Encode the raw name when constructing the path. If a value might already be encoded, establish its provenance rather than guessing: decoding arbitrary input can also change its meaning or create unsafe paths. Avoid manually concatenating untrusted URL fragments.
There is also a difference between path encoding and form encoding. In form-encoded query data, a space is commonly represented by +; in a normal URL path, + is a literal plus sign. Use an API intended for the component you are building rather than replacing spaces with + by hand.
Check the complete download path
A correctly encoded link is only one part of a successful download. Test the actual route and file-serving behavior:
- Inspect the generated link. Check that the origin and path are intact, the space is
%20, Unicode bytes are encoded as expected, and#or?in the name do not become a fragment or query. - Open the link directly. Try the link in current versions of the browsers your audience uses. Check both a normal click and opening the link in a new tab; a link that looks correct in page source can still be altered by application code or a redirect.
- Confirm the server resolves the intended file. Review application routing and server logs. Check whether the framework decodes the path once, whether a proxy normalizes it, and whether the filesystem’s actual name and Unicode normalization match. Visually similar Unicode names can have different underlying character sequences.
- Check response behavior. Confirm the request returns the intended file rather than a 404, an HTML error page, or a redirect to a broken URL. If the browser should save the file under a particular name, verify the response’s download-related headers, such as
Content-Disposition, as well as the route. - Test edge cases. Include spaces, non-ASCII letters,
#,?,%, plus signs, and names with multiple periods. Test characters your application permits, and define how it handles/and malformed Unicode.
Browser URL display and server behavior are not the same thing. A browser can parse and serialize a URL correctly while a reverse proxy, framework router, web server, or filesystem applies different decoding, normalization, or path rules. Check the behavior of the actual hosting stack rather than assuming that encoding alone settles it.
Common Mistakes
- Encoding the whole URL. Encode the file-name component, leaving the scheme, host, separators, and other URL structure intact.
- Using
+for a path space. Use%20in a path segment;+has different conventions in form-encoded data. - Encoding already encoded text.
%becomes%25, often changing the name the server receives. - Treating
%2Fas a portable slash in a file name. Routing layers may reject it or treat it as a separator. Use a route design that avoids this ambiguity. - Assuming a correct link guarantees a download. The route, file permissions, response headers, redirects, and filesystem naming still matter.
- Comparing Unicode names only by appearance. Two visually identical strings can have different Unicode sequences and may not match the stored file name.
Best Practices
- Keep the raw file name separate from its encoded URL representation.
- Encode exactly one path segment with a component-aware API; do not build URLs by indiscriminately escaping strings.
- Define an explicit policy for unsupported characters, malformed Unicode, and slash characters.
- Test representative links through the same proxy, application, and storage layers used in production.
- If authors are editing links manually, an online URL Encoder Decoder can help inspect a component. Verify the result in the real application; a general encoder cannot confirm server routing or download behavior.
Conclusion
To URL encode spaces and special characters in file names, encode the raw file name as one path segment using UTF-8 percent-encoding. Preserve the rest of the URL, avoid encoding the same value twice, and test the final link against the server and filesystem that will serve it.
References
- WHATWG URL Standard — URL parsing and serialization behavior.
- RFC 3986: Uniform Resource Identifier (URI): Generic Syntax — percent-encoding and reserved characters.
- MDN:
encodeURIComponent()— JavaScript component-encoding behavior and exceptions.
