Syntax

Redirect(url, movedPermanently)
2 arguments

The bare-name Redirect global exists only after Platform.Load("core", ...) has run, so call the load first. Once loaded it is usable in that scope and inside nested helper-function bodies that close over it. If you have no Platform.Load in scope, use Platform.Response.Redirect(url, movedPermanently), which needs no Platform.Load.

Parameters

Name Type Required Description
url string Yes The address to send the browser to.
movedPermanently string | boolean | number Yes true / 1 / "true" issue an HTTP 301 Moved Permanently redirect; false / 0 / "false" issue an HTTP 302 Found (“Moved Temporarily”) redirect. Use a temporary redirect unless you are certain the move is permanent — browsers cache 301 responses aggressively and may skip re-checking the original URL.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Parameters —
 *   url               string                         required
 *   movedPermanently  string | boolean | number      required
 * Declared arity: min_args 2, max_args 2.
 *
 * Proves:
 *   1. ARITY. Zero arguments and one argument are rejected with the
 *      engine signature error "Unable to retrieve security descriptor
 *      for this frame." (TypeError). Two arguments are required.
 *   2. url REJECTS null/undefined (Value cannot be null. Parameter
 *      name: url), and rejects plain object / array (signature error).
 *   3. movedPermanently REJECTS null, undefined, empty string, and
 *      plain object (signature error).
 *   4. TYPE-ACCEPTANCE (movedPermanently): string "true"/"false" and
 *      number 1/0 are Accepted with the same 301/302 meaning as
 *      boolean true/false — proven by HTTP status/Location (see
 *      NON-ASSERTABLE). Parameters table and ssjs-data use
 *      string | boolean | number.
 *
 * NON-ASSERTABLE in the body (successful redirects discard output):
 *   - boolean false → HTTP 302; boolean true → HTTP 301 (Location exact).
 *   - string "false" → 302; string "true" → 301.
 *   - number 0 → 302; number 1 → 301.
 *   - DEV: a 3-argument call still redirects (surplus arg ignored) —
 *     declared max_args remains 2; HTTP Location used the first two args.
 *   - url number (e.g. 42) is accepted at runtime (Location /42) but the
 *     url parameter stays typed string (URL params are not widened).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn, expectedFrag) {
    var got = "NO-THROW";
    try { fn(); } catch (ex) { got = "" + ex.message; }
    var ok = got !== "NO-THROW" && got.indexOf(expectedFrag) >= 0;
    Platform.Response.Write((ok ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
var ARITY_ERROR = "Unable to retrieve security descriptor for this frame.";
var NULL_URL = "Value cannot be null.";

assertThrows("0 arguments is rejected (min_args is 2)", function () { return Redirect(); }, ARITY_ERROR);
assertThrows("1 argument is rejected (min_args is 2)", function () { return Redirect("https://example.com/one"); }, ARITY_ERROR);

assertThrows("url=null is rejected", function () { return Redirect(null, false); }, NULL_URL);
assertThrows("url=undefined is rejected", function () { var u; return Redirect(u, false); }, NULL_URL);
assertThrows("url={} is rejected", function () { return Redirect({}, false); }, ARITY_ERROR);
assertThrows("url=[] is rejected", function () { return Redirect([], false); }, ARITY_ERROR);

assertThrows("moved=null is rejected", function () { return Redirect("https://example.com/mn", null); }, ARITY_ERROR);
assertThrows("moved=undefined is rejected", function () { var m; return Redirect("https://example.com/mu", m); }, ARITY_ERROR);
assertThrows("moved='' is rejected", function () { return Redirect("https://example.com/me", ""); }, ARITY_ERROR);
assertThrows("moved={} is rejected", function () { return Redirect("https://example.com/mo", {}); }, ARITY_ERROR);

assert("Parameters chapter reached end without redirecting", "ok", "ok");
</script>

Description

Redirect(url, movedPermanently) sends the visitor’s browser to another URL. Runtime testing proves the bare name is injected by Platform.Load("core", ...) and performs the redirect. It exists only after the load has run, so call Platform.Load first; once loaded it works in that scope and inside nested helper-function bodies that close over it. Its sibling Platform.Response.Redirect() works in any scope and requires no Platform.Load.

Show test script
<script runat="server">
/*
 * Chapter: Description — bare-name Redirect after Platform.Load;
 * Platform.Response.Redirect needs no load.
 *
 * Proves:
 *   1. Before Platform.Load the bare name is undefined; invoking it throws
 *      "Object expected: Redirect". typeof is resolved inside a thunk.
 *   2. After Platform.Load("core", "1.1.5") Redirect is a function.
 *   3. Nested helper-function bodies that close over the loaded scope see
 *      typeof Redirect === "function".
 *   4. Platform.Response.Redirect is a clrmethodinfo and needs no
 *      Platform.Load (checked before the load as well as after).
 *
 * NON-ASSERTABLE: that a successful call performs the HTTP redirect
 * (status/Location) — body is discarded; proven via HTTP probes.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn, expectedFrag) {
    var got = "NO-THROW";
    try { fn(); } catch (ex) { got = "" + ex.message; }
    var ok = got !== "NO-THROW" && got.indexOf(expectedFrag) >= 0;
    Platform.Response.Write((ok ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}

assert("before load typeof Platform.Response.Redirect is clrmethodinfo", typeOfThunk(function () { return typeof Platform.Response.Redirect; }), "clrmethodinfo");
assert("before load typeof Redirect is undefined", typeOfThunk(function () { return typeof Redirect; }), "undefined");
assertThrows("before load Redirect() throws Object expected", function () { return Redirect("https://example.com/pre", false); }, "Object expected: Redirect");

Platform.Load("core", "1.1.5");
assert("after load typeof Redirect is function", typeOfThunk(function () { return typeof Redirect; }), "function");
assert("after load Platform.Response.Redirect is still clrmethodinfo", typeOfThunk(function () { return typeof Platform.Response.Redirect; }), "clrmethodinfo");

function nestedType() { return typeof Redirect; }
assert("nested helper typeof Redirect is function", nestedType(), "function");
</script>

Examples

Bare-name form (same scope as Platform.Load)

Platform.Load("core", "1.1.5");
Redirect("https://www.example.com", false);

Scope-independent form — Platform.Response.Redirect

Platform.Response.Redirect("https://www.example.com", false);

Known bug — redirect inside try/catch

This caveat applies to Platform.Response.Redirect() as well: if a try block contains a redirect, the redirect triggers the catch block. In the example below the intended redirect to salesforce.com is overridden by the catch redirect to example.com:

try {
    Platform.Response.Redirect("https://salesforce.com", false);
} catch (ex) {
    Platform.Response.Redirect("https://example.com", false);
}

Keep redirects out of try blocks, or guard the catch so it does not perform its own redirect.

Show test script
<script runat="server">
/*
 * Chapter: Examples — bare-name form, Platform.Response.Redirect form,
 * and the known try/catch redirect bug.
 *
 * Proves:
 *   1. The bare-name example shape: after Platform.Load("core","1.1.5")
 *      Redirect is a function ready to call (call itself is not issued
 *      here — it would discard the report).
 *   2. Platform.Response.Redirect exists without requiring Core load
 *      (typeof clrmethodinfo) — the scope-independent form in the example.
 *   3. Workaround guidance: the page says keep redirects out of try, or
 *      guard the catch so it does not redirect. Asserted here as the
 *      structural rule that a catch which does NOT redirect leaves the
 *      script able to finish (no Redirect call in this chapter).
 *
 * NON-ASSERTABLE in the body (HTTP Location only):
 *   - Known bug: Redirect / Platform.Response.Redirect inside try is
 *     catchable; a catch that calls Redirect overrides Location.
 *     Proven: try→example.com/try-* then catch→example.com/catch-*
 *     yielded Location=catch-* (302) for both bare Redirect and
 *     Platform.Response.Redirect (AllowAutoRedirect=false).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}

assert("example sibling Platform.Response.Redirect needs no load", typeOfThunk(function () { return typeof Platform.Response.Redirect; }), "clrmethodinfo");

Platform.Load("core", "1.1.5");
assert("example bare-name Redirect is a function after load", typeOfThunk(function () { return typeof Redirect; }), "function");

var catchGuarded = "no";
try {
    catchGuarded = "try-ran";
} catch (ex) {
    catchGuarded = "catch-ran";
}
assert("workaround: a try without Redirect finishes without catch redirect", catchGuarded, "try-ran");
</script>

See Also