URL encoding and decoding
The codec system
Scramjet uses a configurable codec to encode and decode URLs. The codec is a pair of functions defined in your configuration:The default codec uses
encodeURIComponent and decodeURIComponent, but you can use any encoding scheme (base64, custom obfuscation, etc.).How encoding works
When you navigate to a URL through Scramjet:encodeUrl() method transforms it:
1
Parse the URL
The URL is parsed into a URL object.
2
Extract and encode hash
The hash fragment is separated and encoded independently:
3
Encode the URL
The main URL (without hash) is encoded:
4
Result
How decoding works
When the Service Worker intercepts a request, it decodes the URL:unrewriteUrl() function in src/shared/rewriters/url.ts reverses the process:
1
Check for special protocols
Handle special URL types first:
2
Handle blob/data URLs
3
Decode the main URL
URL rewriting in content
Once content is fetched, all URLs within it must be rewritten to point through the proxy.The rewriteUrl function
TherewriteUrl() function is the core rewriter, defined in src/shared/rewriters/url.ts:
- url: The URL to rewrite (absolute or relative)
- meta: Context about the current page (origin, base URL, frame names)
URLMeta context
TheURLMeta object provides context for rewriting:
The
base URL can differ from origin if the page contains a <base> tag. This is updated dynamically during HTML rewriting.Special URL handling
rewriteUrl() handles different URL schemes:
javascript: URLs
javascript: URLs
JavaScript URLs are rewritten by rewriting the JavaScript code:
blob: URLs
blob: URLs
Blob URLs are prefixed with the proxy origin:This routes blob fetches through the Service Worker so they can be rewritten.
data: URLs
data: URLs
Data URLs are also prefixed:
mailto: and about: URLs
mailto: and about: URLs
These are returned unchanged:
HTTP(S) URLs
HTTP(S) URLs
Regular URLs are resolved against the base, then encoded:
Relative URL resolution
Relative URLs are resolved againstmeta.base:
Where URLs are rewritten
Scramjet rewrites URLs in multiple places throughout the stack:HTML rewriting
Insrc/shared/rewriters/html.ts, URLs in HTML attributes are rewritten:
htmlRules array (from src/shared/htmlRules.ts) defines which attributes to rewrite:
Special HTML cases
JavaScript rewriting
JavaScript rewriting is more complex. It uses an oxc-based WASM rewriter to:- Parse the JavaScript AST
- Identify API calls that accept URLs
- Wrap those calls with runtime functions that rewrite URLs
$scramjet$rewrite function calls rewriteUrl() at runtime with the current page’s metadata.
CSS rewriting
Insrc/shared/rewriters/css.ts, URLs in CSS are rewritten:
background-image: url(...)@import url(...)@font-face { src: url(...) }- etc.
Client-side interception
In theScramjetClient, DOM APIs are intercepted to rewrite URLs at runtime:
URL preservation
Scramjet preserves original URLs in HTML usingscramjet-attr-* attributes:
- Debugging and inspection
- Restoring original URLs when needed
- Compatibility with scripts that read attributes directly
Hash handling
Hash fragments require special handling because:- They’re not sent to the server
- They’re used for client-side routing
- They need to work with browser navigation APIs
window.location.hashworks correctly- Hash-based routers work
- The browser’s back/forward buttons work
Performance considerations
Common pitfalls
Next steps
Configuration
Learn about codec configuration and other options
Service Worker
See how URLs are decoded in the Service Worker