# How Shadowsocks‑Windows Parses ss:// URLs: Import, Export, and Legacy Support

> Discover how Shadowsocks-Windows parses ss:// URLs using regex, handling imports, exports, and legacy formats. Understand server configuration and deprecation warnings.

- Repository: [shadowsocks/shadowsocks-windows](https://github.com/shadowsocks/shadowsocks-windows)
- Tags: internals
- Published: 2026-03-05

---

**Shadowsocks‑Windows uses a regex‑driven parser in [`Model/Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Model/Server.cs) to decode Base64‑encoded `ss://` payloads into server configuration objects, exports canonical URLs via `ToString()`, and detects legacy plain‑text formats to emit deprecation warnings through `ShadowsocksController`.**

The `shadowsocks/shadowsocks-windows` repository implements a robust URL handling system for the Shadowsocks protocol. Understanding how the client handles `ss://` server URL parsing—including import, export, and backward‑compatible legacy support—is essential for developers integrating with the Windows client or troubleshooting configuration imports.

## Core URL Parsing Infrastructure in Model/Server.cs

All `ss://` processing logic resides in **[`shadowsocks-csharp/Model/Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Model/Server.cs)**. The file defines a static regex that acts as the entry point for every import operation.

### The UrlFinder Regex Pattern

The parser first validates the URL structure using a case‑insensitive regular expression that captures the Base64 payload and an optional remark tag:

```csharp
private static readonly Regex UrlFinder = 
    new Regex(
        @"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", 
        RegexOptions.IgnoreCase
    );

```

This pattern ensures the string starts with `ss://`, extracts the **Base64‑encoded payload** (named group `base64`), and isolates the URL‑fragment tag (named group `tag`) when present. Query parameters such as `?plugin=obfs-local` appear after the payload but are handled separately during string splitting.

### Decoding the Base64 Payload

Once the regex matches, the client decodes the captured value using `Convert.FromBase64String` (or its URL‑safe variant) to reveal a UTF‑8 string formatted as `method:password@host:port`. If decoding fails, the parser rejects the URL as malformed and returns `null`.

## Import Workflow: From ss:// String to Server Object

The `Server.Parse` method orchestrates the transformation from URL string to populated object:

1. **Trim whitespace** from the input string.
2. **Execute `UrlFinder.Match`** to locate the payload and tag.
3. **Base64‑decode** the payload into `method:password@host:port`.
4. **Split at the first `@`** to separate credentials from the host portion.
5. **Parse the left side** (`method:password`) by splitting at the first `:` into `Method` and `Password`.
6. **Parse the right side** (`host:port`) by splitting at the last `:` into `Host` and `Port` (converting the port to an integer).
7. **Store the raw URL** in `_serverUrl` for round‑trip fidelity.
8. **Return** the fully populated `Server` instance.

Below is a practical example of importing a modern URL with a remark:

```csharp
using Shadowsocks.Model;

string modernUrl = "ss://YWVzLTI1Ni1jZmI6c2VjcmV0QDE5Mi4xNjguMS4xOjgzODg=#Tokyo-Node";
Server srv = Server.Parse(modernUrl);

Console.WriteLine(srv.Method);   // aes-256-cfb
Console.WriteLine(srv.Password); // secret
Console.WriteLine(srv.Host);     // 192.168.1.1
Console.WriteLine(srv.Port);     // 8388
Console.WriteLine(srv.Tag);      // Tokyo-Node

```

The parser also preserves query strings. If the URL contains `?plugin=obfs-local;obfs=http`, that substring remains attached to the internal `_serverUrl` field and is re‑appended during export.

## Export Workflow: Generating Canonical ss:// URLs

When the UI or command line needs to share a server configuration, the `Server` class reconstructs the canonical URL through its overridden `ToString` method.

### Server.ToString() Implementation

Located around line 118 in [`Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Server.cs), the export logic:

1. Concatenates `Method`, `Password`, `Host`, and `Port` into `method:password@host:port`.
2. Base64‑encodes the resulting UTF‑8 bytes.
3. Prefixes the encoded string with `ss://`.
4. Appends the `#tag` fragment if the `Tag` property is non‑empty.

The implementation returns a single interpolated string:

```csharp
public override string ToString()
{
    // url = Base64Encode($"{Method}:{Password}@{Host}:{Port}")
    // tag = string.IsNullOrEmpty(Tag) ? "" : $"#{Tag}"
    return $"ss://{url}{tag}";
}

```

Because the original `_serverUrl` is preserved during import, exporting a legacy URL that was imported will still produce a **modern** Base64‑encoded canonical form, effectively normalizing the configuration on every save.

## Legacy URL Support and Deprecation Warnings

Versions prior to v4 occasionally used **plain‑text** `ss://` URLs where the payload was not Base64‑encoded (e.g., `ss://aes-256-cfb:password@1.2.3.4:8388`). The current codebase maintains backward compatibility but flags these inputs as deprecated.

### Detection and Warning Mechanism

In **[`ShadowsocksController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/ShadowsocksController.cs)** around line 454, the controller checks whether the parsed URL adheres to the modern standard. If the string fails the `UrlFinder` regex validation or if internal logic detects a non‑Base64 payload structure, the application:

1. Still attempts to parse the legacy format using fallback string splitting.
2. Logs a user‑visible warning message stating that legacy URL support will be removed in v5.
3. Proceeds with the import so the user’s configuration is not lost.

Developers should treat this as a transitional feature; automated tools generating `ss://` links should always emit Base64‑encoded payloads to avoid triggering the deprecation warning.

## Windows Protocol Association

End users can click `ss://` links in browsers because the client registers itself as the protocol handler. The registration logic lives in **[`View/MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/View/MenuViewController.cs)** near line 289. When the user selects **"Associate ss:// Links"** from the tray menu, the code writes the necessary registry keys to map the `ss://` scheme to the Shadowsocks executable path, enabling one‑click imports directly from web pages.

## Unit Test Coverage for URL Edge Cases

The **[`test/UrlTest.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/test/UrlTest.cs)** file contains comprehensive assertions covering:

- Modern Base64 URLs with and without tags.
- URLs containing plugin query parameters (`?plugin=...`).
- Malformed Base64 strings (expecting `null` returns).
- Legacy plain‑text formats (verifying correct parsing and warning emission).
- Edge cases such as trailing slashes and empty passwords.

These tests ensure that changes to the parsing regex or decoding logic do not break existing user configurations.

## Summary

- **Canonical parsing** relies on the static `UrlFinder` regex in [`Model/Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Model/Server.cs) to isolate Base64 payloads and optional tags before decoding.
- **Field extraction** splits the decoded `method:password@host:port` string to populate the `Server` object properties.
- **Export normalization** occurs in `Server.ToString()`, which always generates modern Base64‑encoded URLs regardless of the original import format.
- **Legacy compatibility** is handled as a fallback with explicit deprecation warnings in [`ShadowsocksController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/ShadowsocksController.cs), slated for removal in v5.
- **Protocol association** is managed through [`MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/MenuViewController.cs), allowing Windows to launch the client when users click `ss://` links.
- **Test coverage** in [`UrlTest.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UrlTest.cs) validates both contemporary and legacy URL formats, including plugin parameters and error conditions.

## Frequently Asked Questions

### How does shadowsocks‑windows handle invalid Base64 in ss:// URLs?

When the Base64 payload cannot be decoded (invalid characters, wrong padding, or corruption), `Convert.FromBase64String` throws an exception that the `Server.Parse` method catches. The parser returns `null`, and the calling code in the controller typically discards the malformed URL or presents an error to the user without crashing the application.

### What is the difference between modern and legacy ss:// URL formats?

Modern formats encode the entire `method:password@host:port` segment as a Base64 string placed immediately after `ss://`, optionally followed by a `#tag` fragment. Legacy formats placed this information in plain text (e.g., `ss://aes-256-cfb:pass@host:port`). The modern client parses both but emits a deprecation warning for legacy plain‑text inputs because they will be unsupported in version 5.

### Can shadowsocks‑windows export server configurations that include plugin parameters?

Yes. If the original imported URL contained a query string such as `?plugin=obfs-local;obfs=http;obfs-host=google.com`, the raw `_serverUrl` field preserves that substring. When `ToString()` is called, it reconstructs the Base64 payload and appends the preserved query string, generating a fully functional URL that retains all plugin options.

### Where is the ss:// protocol handler registered on Windows?

The registration occurs in [`View/MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/View/MenuViewController.cs) around line 289. Selecting the **"Associate ss:// Links"** menu item writes registry entries under `HKEY_CLASSES_ROOT\ss` that map the protocol to the Shadowsocks executable. This allows the operating system to route `ss://` link clicks directly to the client for automatic import.