Scramjet uses URL codecs to encode real URLs into proxy URLs and decode them back. You can customize this behavior by providing your own encoding and decoding functions.
Default codec
By default, Scramjet uses simple URL encoding:
// Default encoder
function encode ( url ) {
if ( ! url ) return url ;
return encodeURIComponent ( url );
}
// Default decoder
function decode ( url ) {
if ( ! url ) return url ;
return decodeURIComponent ( url );
}
With the default codec and prefix /scramjet/, a URL like https://example.com becomes:
/scramjet/https%3A%2F%2Fexample.com
Creating a custom codec
Custom codecs are defined when creating the ScramjetController:
const { ScramjetController } = $scramjetLoadController ();
const scramjet = new ScramjetController ({
prefix: '/scramjet/' ,
codec: {
encode : ( url ) => {
// Your custom encoding logic
return btoa ( url );
},
decode : ( url ) => {
// Your custom decoding logic
return atob ( url );
},
},
});
await scramjet . init ();
The encoder and decoder functions must be inverse operations - what you encode must be decodable back to the original URL.
Common codec implementations
Base64 encoding
Base64 encoding makes URLs more obfuscated:
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => {
if ( ! url ) return url ;
return btoa ( url );
},
decode : ( url ) => {
if ( ! url ) return url ;
return atob ( url );
},
},
});
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => {
if ( ! url ) return url ;
// Convert to URL-safe base64
return btoa ( url )
. replace ( / \+ / g , '-' )
. replace ( / \/ / g , '_' )
. replace ( /=/ g , '' );
},
decode : ( url ) => {
if ( ! url ) return url ;
// Convert back from URL-safe base64
let base64 = url
. replace ( /-/ g , '+' )
. replace ( /_/ g , '/' );
// Add padding
while ( base64 . length % 4 ) {
base64 += '=' ;
}
return atob ( base64 );
},
},
});
XOR cipher encoding
Add simple obfuscation with an XOR cipher:
function xorCipher ( str , key ) {
let result = '' ;
for ( let i = 0 ; i < str . length ; i ++ ) {
result += String . fromCharCode (
str . charCodeAt ( i ) ^ key . charCodeAt ( i % key . length )
);
}
return result ;
}
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => {
if ( ! url ) return url ;
const key = 'mySecretKey' ;
const encrypted = xorCipher ( url , key );
return btoa ( encrypted )
. replace ( / \+ / g , '-' )
. replace ( / \/ / g , '_' )
. replace ( /=/ g , '' );
},
decode : ( url ) => {
if ( ! url ) return url ;
const key = 'mySecretKey' ;
// Add padding
let base64 = url
. replace ( /-/ g , '+' )
. replace ( /_/ g , '/' );
while ( base64 . length % 4 ) {
base64 += '=' ;
}
const encrypted = atob ( base64 );
return xorCipher ( encrypted , key );
},
},
});
XOR cipher provides minimal security and should not be relied upon for actual encryption. Use it only for obfuscation.
Plain encoding
For maximum compatibility and debugging, use plain text URLs:
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => url ,
decode : ( url ) => url ,
},
});
Plain encoding requires special web server configuration to handle URLs containing :// and other special characters.
Custom delimiter encoding
Use a custom delimiter instead of URL encoding:
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => {
if ( ! url ) return url ;
// Replace :// with a custom delimiter
return url . replace ( '://' , '---' );
},
decode : ( url ) => {
if ( ! url ) return url ;
// Restore ://
return url . replace ( '---' , '://' );
},
},
});
// https://example.com becomes /scramjet/https---example.com
Hash fragment handling
Scramjet automatically handles URL hash fragments separately:
// Input URL: https://example.com/page#section
// The codec receives: https://example.com/page
// The hash is encoded separately: #section
Your codec doesn’t need to handle hash fragments - Scramjet manages them automatically:
const url = new URL ( 'https://example.com/page#section' );
// Scramjet encodes the hash separately
const encodedHash = codecEncode ( url . hash . slice ( 1 ));
const realHash = encodedHash ? '#' + encodedHash : '' ;
// The URL is encoded without the hash
url . hash = '' ;
const encodedUrl = config . prefix + codecEncode ( url . href ) + realHash ;
Using the controller’s encode/decode methods
The ScramjetController provides helper methods for encoding and decoding URLs:
const { ScramjetController } = $scramjetLoadController ();
const scramjet = new ScramjetController ({
prefix: '/scramjet/' ,
codec: {
encode : ( url ) => btoa ( url ),
decode : ( url ) => atob ( url ),
},
});
await scramjet . init ();
// Encode a URL
const proxiedUrl = scramjet . encodeUrl ( 'https://example.com' );
console . log ( proxiedUrl ); // /scramjet/aHR0cHM6Ly9leGFtcGxlLmNvbQ
// Decode a URL
const realUrl = scramjet . decodeUrl ( proxiedUrl );
console . log ( realUrl ); // https://example.com
Use these methods instead of calling your codec functions directly to ensure proper prefix handling.
Protocol handling
Scramjet only proxies HTTP and HTTPS URLs. Other protocols are passed through unchanged:
const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => btoa ( url ),
decode : ( url ) => atob ( url ),
},
});
await scramjet . init ();
// HTTP/HTTPS URLs are encoded
scramjet . encodeUrl ( 'https://example.com' );
// -> /scramjet/aHR0cHM6Ly9leGFtcGxlLmNvbQ
// Other protocols pass through
scramjet . encodeUrl ( 'mailto:[email protected] ' );
// -> mailto:[email protected]
scramjet . encodeUrl ( 'javascript:alert(1)' );
// -> javascript:alert(1)
Testing your codec
Here’s a test suite for validating your custom codec:
function testCodec ( encoder , decoder ) {
const testUrls = [
'https://example.com' ,
'https://example.com/path?query=value' ,
'https://example.com:8080/path' ,
'http://subdomain.example.com/path' ,
'https://example.com/path?q=hello world&lang=en' ,
];
console . log ( 'Testing codec...' );
for ( const url of testUrls ) {
const encoded = encoder ( url );
const decoded = decoder ( encoded );
if ( url === decoded ) {
console . log ( '✓' , url );
} else {
console . error ( '✗' , url );
console . error ( ' Expected:' , url );
console . error ( ' Got:' , decoded );
}
}
}
// Test your codec
testCodec (
( url ) => btoa ( url ),
( url ) => atob ( url )
);
The codec functions are called frequently during page loads. Keep them fast: // Fast - simple operations
encode : ( url ) => btoa ( url )
// Slower - complex operations
encode : ( url ) => {
// Multiple regex operations
return url
. replace ( /:/ g , '%3A' )
. replace ( / \/ / g , '%2F' )
. replace ( / \? / g , '%3F' )
// ... many more replacements
}
Some codecs increase URL length significantly: const url = 'https://example.com' ;
// URL encoding (default): 25 chars
encodeURIComponent ( url ); // https%3A%2F%2Fexample.com
// Base64: 28 chars
btoa ( url ); // aHR0cHM6Ly9leGFtcGxlLmNvbQ==
// XOR + Base64: 28+ chars
Long URLs may cause issues with some servers or browsers.
Always handle edge cases: const scramjet = new ScramjetController ({
codec: {
encode : ( url ) => {
if ( ! url ) return url ;
try {
return btoa ( url );
} catch ( error ) {
console . error ( 'Encoding failed:' , error );
// Fallback to default encoding
return encodeURIComponent ( url );
}
},
decode : ( url ) => {
if ( ! url ) return url ;
try {
return atob ( url );
} catch ( error ) {
console . error ( 'Decoding failed:' , error );
// Fallback to default decoding
return decodeURIComponent ( url );
}
},
},
});
Basic setup Learn how to configure Scramjet
Configuration flags Explore other configuration options