← Blog Posts
May 14, 2026 4 min read Salesforce

Fixing CloudPagesURL When SFMC Click Tracking Breaks Your Links

Every Salesforce Marketing Cloud developer eventually hits this one. You build a CloudPage, a preference center or an unsubscribe page, and link to it from an email using CloudPagesURL(). It works perfectly in preview and in test sends. Then the live send goes out, subscribers click the link, and the page can’t find any of its parameters. RequestParameter() comes back empty. The page errors out or falls back to its “missing data” state.

Nothing is wrong with your CloudPage. The problem is SFMC’s click tracking corrupting the URL before the subscriber ever reaches it.

Why CloudPagesURL matters in the first place

CloudPagesURL() is the right way to link from an email to a CloudPage. It generates the page URL with an encrypted query string that carries subscriber context: subscriber key, email address, job ID, and any custom parameters you pass in.

%%=CloudPagesURL(1234, "brand", @brand, "source", "email")=%%

On the receiving CloudPage, RequestParameter() decrypts those values automatically:

%%[
SET @subscriberKey = AttributeValue("_subscriberkey")
SET @brand         = RequestParameter("brand")
]%%

That encryption is the feature. It means subscribers can’t tamper with the query string, and you’re not passing email addresses around in plain text. It’s also exactly what makes the link fragile.

When a send goes out with click tracking enabled, SFMC rewrites every href in the email to route through its tracking domain. The subscriber clicks the tracking URL, SFMC logs the click, then redirects to your original destination.

For a normal URL, this round trip is harmless. For a CloudPagesURL() link, it isn’t. The encrypted query string contains characters that don’t survive the rewrite and redirect cleanly. The tracking layer re-encodes parts of the URL, and by the time the subscriber lands on your CloudPage, the encrypted payload no longer decrypts. RequestParameter() returns nothing.

This is why the problem hides during development. Previews and some test sends skip the click tracking wrap, so the link works. The corruption only shows up under real send conditions, which is the worst possible time to find it.

The fix: URLEncode plus RedirectTo

The workaround takes two steps. First, build the CloudPage URL in the header of your email (or template) and URL-encode the whole thing so the encrypted query string survives the tracking rewrite intact:

%%[
VAR @FullURL
SET @FullURL = URLEncode(Concat(CloudPagesURL(1234), ''))
]%%

Replace 1234 with your CloudPage’s page ID. The Concat() with an empty string forces CloudPagesURL() to resolve to a string before URLEncode() runs, which matters because URLEncode() needs the finished URL, not the function reference.

Second, use RedirectTo() as the href wherever the button or link appears in the body:

<a href="%%=RedirectTo(@FullURL)=%%">Manage your preferences</a>

That’s the whole fix. RedirectTo() tells SFMC “this value is already a complete URL, resolve it at send time and leave it alone.” Click tracking still wraps the link and still logs the click, but the encoded query string passes through the redirect without being mangled. When the subscriber lands on the CloudPage, RequestParameter() decrypts everything normally. No changes are needed on the CloudPage side.

If you’re passing custom parameters, they go in the CloudPagesURL() call exactly as before:

%%[
VAR @FullURL
SET @FullURL = URLEncode(Concat(CloudPagesURL(1234, "brand", @brand, "unsubscribeFromAll", "1"), ''))
]%%

How to verify it actually works

Because this bug only appears under real send conditions, test it under real send conditions:

  1. Send to a test list with click tracking enabled, not just a preview or a “send test” from Content Builder.
  2. Click the link from the delivered email and confirm the CloudPage receives its parameters. A temporary debug block helps:
%%[
IF @debug == 1 THEN
    output(concat("<br>subscriberKey: ", @subscriberKey))
    output(concat("<br>brand: ", @brand))
ENDIF
]%%
  1. Check your tracking reports afterward to confirm the click still logged. It will. RedirectTo() preserves tracking; it only protects the destination URL.

The takeaway

CloudPagesURL() and click tracking both do their jobs correctly. They just weren’t designed to do them to the same URL. Encode the full URL in the header, hand it to RedirectTo() in the body, and you keep the encrypted subscriber context, the click tracking data, and your sanity. Two lines of AMPscript, and the preference center link that worked in preview also works in production.