← Back to Blog

Base64 vs Base64URL in .NET: When + and / Break URLs

Base64 can contain + and /, which behave differently in URL paths and query strings. Tested ASP.NET Core examples and the Base64URL fix.

Pankaj Kumar
Senior Software Engineer — .NET, Blazor, ASP.NET Core
Articles are drafted with AI assistance and reviewed by the author. Code samples and results are run before publication, and each article says how its results were produced.
Published 03 Jul 2026· Last reviewed 14 Oct 2026· 6 min read · About the author →
All results below were produced on .NET 8.0.31 and ASP.NET Core 8 on 14 Oct 2026, using a local Kestrel server for the URL tests.
Key takeaways
  • Convert.ToBase64String always produces standard Base64, which can contain + and /
  • In ASP.NET Core a + in a URL path is kept as +, but a + in a query string is decoded as a space
  • A / in a path splits it into extra segments, and an encoded %2F reaches your route as the literal text %2F, so a lookup by the original key fails
  • Base64URL swaps + for - and / for _ and drops the = padding; WebEncoders.Base64UrlEncode does this in one call
  • Plain ASCII text rarely produces + or /, but random or binary data (hashes, tokens, encrypted bytes) does so very often
Table of Contents

Standard Base64 and Base64URL are different alphabets

Standard Base64 (RFC 4648 section 4) uses A-Z a-z 0-9 + / and pads with =. Base64URL (section 5) replaces + with - and / with _, and padding is usually omitted. Convert.ToBase64String in .NET always produces the standard form; it has no URL-safe option. JWTs use Base64URL for their segments, which is why you never see + or / in a token.

What the two variants look like

Real output from Convert.ToBase64String and WebEncoders.Base64UrlEncode:

InputStandard Base64Base64URL
user42:invoice?.pdfdXNlcjQyOmludm9pY2U/LnBkZg==dXNlcjQyOmludm9pY2U_LnBkZg
???Pz8/Pz8_
bytes FB FF BF+/+/-_-_
user42:report>final.pdfdXNlcjQyOnJlcG9ydD5maW5hbC5wZGY=dXNlcjQyOnJlcG9ydD5maW5hbC5wZGY

The last row shows the point: many inputs encode to the same text in both variants, so a bug can be invisible until the first input that hits character 62 or 63 of the alphabet.

How often do + and / appear?

It depends entirely on what you encode:

  • Random or binary bytes: in 20,000 random 18-byte values (24 Base64 characters), 53.6% contained a + or /. Hashes, GUID bytes, random tokens and encrypted payloads behave like this, so for these the problem shows up almost immediately.
  • Plain ASCII text: in 20,000 synthetic keys of the form userN:IMG_N.ext, none contained + or /. Letters and digits rarely produce them. Punctuation such as ? can, as the table shows.

That is why bugs of this kind tend to appear "only sometimes": they depend on the data, and tests with simple names will pass.

What ASP.NET Core does with them

A local Kestrel server was started with a route /files/{key} and a query endpoint, and each variant was requested:

RequestResponse
/files/abc+def200, key = abc+def (the + is kept in a path)
/files/abc%2Bdef200, key = abc+def
/files/abc/def404 (the / created an extra path segment)
/files/abc%2Fdef200, key = abc%2Fdef (not decoded to a /)
/files/abc==200, key = abc==
/q?k=abc+defvalue = abc def (+ became a space in the query string)
/q?k=abc%2Bdefvalue = abc+def

Three practical consequences:

  • A / in a Base64 value used as a path segment makes the route miss (404), and the encoded form %2F does not decode back to /, so your lookup by the original key fails.
  • A + is harmless in a path in ASP.NET Core but turns into a space in a query string unless you percent-encode it. Other proxies, clients and servers may treat it differently, so do not rely on it.
  • Padding = was accepted in a path here, but dropping it keeps URLs shorter and is the usual Base64URL convention.

The fix: use Base64URL

using Microsoft.AspNetCore.WebUtilities;
using System.Text;

// Encode: never produces +, / or =
static string ToUrlKey(string text) =>
    WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(text));

// Decode
static string FromUrlKey(string key) =>
    Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(key));

// Round trip: FromUrlKey(ToUrlKey("user42:invoice?.pdf")) == "user42:invoice?.pdf"

If you cannot reference Microsoft.AspNetCore.WebUtilities, the manual version produces the same alphabet (tested: the bytes FB FF BF give -_-_):

static string ManualUrlSafe(byte[] bytes) =>
    Convert.ToBase64String(bytes)
        .TrimEnd('=')
        .Replace('+', '-')
        .Replace('/', '_');

Newer .NET versions (.NET 9 and later) also include a built-in Base64Url class in System.Buffers.Text.

A simple rule

Use Convert.ToBase64String for binary data inside JSON, email or other non-URL contexts. Use Base64URL for anything that ends up in a URL, a file name, or a cookie value. If you paste a value into the Base64 Encoder and see + or / in the output, switch to the URL-safe form before using it in a link.

Update, 14 Oct 2026: an earlier version of this article told the topic as a first-person incident, claimed that a + in a URL path is decoded as a space, and quoted a 4% failure rate. None of that was based on a real event or a measurement, so the article has been rewritten around tested results.

Try the free tool

Base64 Encoder / Decoder

Encode text to standard Base64 or decode Base64 strings back to plain text. Useful for inspecting encoded values and checking whether a string contains URL-unsafe characters.

Open Base64 Encoder / Decoder