Another one from the “helping out my partner” stack…
She uses ActiveCampaign to set up sign-up forms for mailing lists, promotions and products. These forms are then embedded in her Wix site, as an <iframe> embed.
ActiveCampaign’s inline forms have an “On Submit → Open URL” option, but we have found that new subscribers still land on ActiveCampaign’s generic “Thanks for signing up!” confirmation page rather than the custom thank-you page she set up.
We raised this bug with ActiveCampaign over 12 months ago, but nothing seems to have been fixed, and thus it doesn’t seem like there will be a fix coming any time soon.
After a bit of troubleshooting, we came up with a workaround. Despite it feeling a bit “hacky” and not being the nicest thing in the world, it seems to work reliably without breaking tags, automations, etc.
Why the redirect fails
The ActiveCampaign inline embed works like this:
- On submit, it fires a JSONP request to
proc.php?…&jsonp=trueusing a dynamically inserted<script>tag - That script executes inside the iframe context (if you’re in one)
- On success, the JSONP response looks something like:
window.top.location.href = "https://yoursite.activehosted.com/f/confirm.php?id=…";
This lil’ nugget takes the subscriber to the “default” success page—not the one you’ve built and specified in ActiveCampaign’s form building tools.
Because that line runs before the _show_thank_you handler ever fires, even a correctly-configured “Open URL” setting (in ActiveCampaign) gets bypassed.
ActiveCampaign form setup
First, make sure the form itself is configured correctly:
- Website → Forms → open the form
- Options → On Submit → Open URL
- Paste your full thank-you URL
- Save
The client-side override
Paste the Full Embed HTML that ActiveCampaign gives you into your page or HTML component as usual, then append the following script block after it. The order matters—this script wraps functions that the ActiveCampaign code has to define first.
That is, leave the stock ActiveCampaign embed script unchanged. Only add the override block at the end—i.e.
<script>
...ActiveCampaign embed code...
</script>
<script>
...the override script (below)...
</script>
Replace the text in https://yoursite.com/thank-you with your actual thank-you page URL.
<script>
(function () {
const THANK_YOU_URL = 'https://yoursite.com/thank-you';
const ERROR_GRActiveCampaignE_PERIOD_MS = 800; // time to let a delayed _show_error land before redirecting
const redirectToThankYou = () => {
(window.top || window).location.href = THANK_YOU_URL;
};
const acLoadScript = window._load_script;
const acShowError = window._show_error;
let submissionFailed = false;
// CAPTCHA/validation failures from proc.php don't call _show_error
// synchronously—see "CAPTCHA fails but thank-you shows anyway" below.
// Hooking this directly is the reliable signal; checking the DOM at
// onload time is not.
window._show_error = function (...args) {
submissionFailed = true;
return acShowError?.apply(this, args);
};
window._show_thank_you = () => {
redirectToThankYou();
};
window._load_script = (url, callback, isSubmit) => {
if (isSubmit) {
submissionFailed = false;
return acLoadScript.call(this, url, () => {
// Give a delayed _show_error time to arrive before deciding this
// submission actually succeeded.
setTimeout(() => {
if (!submissionFailed) {
redirectToThankYou();
}
}, ERROR_GRActiveCampaignE_PERIOD_MS);
callback?.();
}, isSubmit);
}
if (url && /confirm\.php|activehosted\.com\/f\//.test(url)) {
redirectToThankYou();
callback?.();
return;
}
return acLoadScript.call(this, url, callback, isSubmit);
};
window._form_callback = () => {
if (!submissionFailed) redirectToThankYou();
};
})();
</script>
Additional notes
| Piece | Purpose |
|---|---|
_load_script + isSubmit | Lets proc.php process the submission normally; waits a short grace period, then redirects only if _show_error hasn’t fired |
_load_script + confirm.php URL | Intercepts the JSONP response when it tries to navigate to ActiveCampaign’s confirmation page |
_show_thank_you | Redirects when ActiveCampaign calls its inline success handler |
_form_callback | Belt-and-braces redirect at the end of success handling |
window.top | Breaks out of any iframe so the whole page navigates, not just the embed |
Don’t skip proc.php
The submit flow calls:
_load_script('https://yoursite.activehosted.com/proc.php?' + serialized + '&jsonp=true', null, true);
That GET request is the submission to the ActiveCampaign system. Tags, automations, and webhooks all run when ActiveCampaign receives this request. The isSubmit branch in our override waits for proc.php to finish, then redirects. If you modify this script, never return early on isSubmit without calling acLoadScript—that will skip proc.php entirely and none of your ActiveCampaign actions will run. (Go on, ask me how I know this… 🙄)
Checklist for a new form
- Configure form in ActiveCampaign: Submit → Open URL → your thank-you page
- Configure the Confirmation action too if existing contacts should get the same redirect/tag
- Paste ActiveCampaign
Full EmbedHTML into your page/component - Append the override script with your
THANK_YOU_URL - Test with a new email address to verify redirect and that tags/automations fire
- For existing contacts: test via the Confirmation path or delete the contact and retest
(Bonus Tip: How you can use Gmail + addresses for your testing.)
Troubleshooting
Still seeing ActiveCampaign’s generic confirmation page
Possible reasons:
- Override is placed before the ActiveCampaign embed (it must come after)
- Override is missing the
if (isSubmit)onload redirect — use the full script above - Existing contact hitting the Confirmation path, which may point to a different URL
Redirect works but only appears inside the iframe
- Missing
window.top— use(window.top || window).location.href
Tags/automations/webhooks not firing
- Early return on
isSubmitis blockingproc.php—use the full script above - Existing contact: Submit actions don’t run for them; configure the
Confirmationaction instead - The automation trigger is
Submit-only; add a matchingConfirmationtrigger
Mobile keyboard “Send”/“Go” submits, but nothing reaches ActiveCampaign
The confirm.php/activehosted.com/f/ check must run after the isSubmit check, not before. On some mobile flows, ActiveCampaign’s own submit call (isSubmit: true) can carry a URL matching that pattern. If the pattern check runs first, it short-circuits and redirects without ever calling acLoadScript—so proc.php never fires and the submission is silently dropped, even though the thank-you page still shows. Check isSubmit first, unconditionally, before testing the URL pattern (see script above).
CAPTCHA (or other validation) fails, but the thank-you page shows anyway
A failed CAPTCHA doesn’t call ActiveCampaign’s _show_error() synchronously. The failure response first reloads the reCAPTCHA widget (a separate, async _load_script call to Google’s API), and _show_error only fires once that finishes loading. By the time proc.php’s own script tag fires onload, the error hasn’t landed in the DOM yet—so checking for a ._form_error element at that exact moment is too early and will miss it every time.
The Fix: hook _show_error directly (it’s the reliable signal, not a DOM side-effect you have to poll for) and add a short grace period (a few hundred ms) before redirecting, so a delayed error has time to arrive and cancel it. See script above.
If you still see false-positive redirects, increase ERROR_GRActiveCampaignE_PERIOD_MS—reCAPTCHA’s API load time varies with network conditions.
This is a different bug from the mobile keyboard issue above. Both involve isSubmit, but this one happens in a normal browser as well as on mobile, and it needs timing/flag-based handling rather than a reordering of the existing checks.