A Form Handler is a web page which sole purpose is to process the data sent from a web form and redirect back or to another page.
This technique allows us to avoid the flawed Smart Capture feature and grants the freedom to create any kind of forms and make them do various actions including creating multiple records and triggered emails.
How it works
- Web Form page sends a field value (Name) to the Form Handler page.
- Form Handler redirects back to the Web Form page with Name and Success parameters encoded in the URL.
- Web Form displays a success message due to the presence of Success parameter in the URL.
Now, let’s build it!
Web Form
For the purpose of this exercise the form is kept as simple as possible.
<form action="%%=CloudPagesURL(1977)=%%" method="post" class="form centered">
<input name="Name" type="text" placeholder="Your name" required>
<button>Send</button>
</form>
1977 is the Page ID of the Form Handler page.
Note that using CloudPagesURL method for generating the URLs is highly recommended as it automatically encodes the URL parameters.
Instead of creating a success page, we are going to replace the form by a success message if a parameter is present in the URL (in this case, the Form Handler is sending Success and Name parameters).
%%[
VAR @Name
SET @Name = RequestParameter("Name")
SET @Success = RequestParameter("Success")
IF @Success != 1 THEN
]%%
<form action="%%=CloudPagesURL(1977)=%%" method="post" class="form centered">
<input name="Name" type="text" placeholder="Your name" required>
<button>Send</button>
</form>
%%[ ELSE ]%%
<div>
<p>Thank you %%=v(@Name)=%%!</p>
</div>
%%[ ENDIF ]%%
And for the finishing touches, let’s add some styling and Javascript validation rules to make the form more appealing and foolproof.
<!DOCTYPE html>
<html>
<head>
<title>Form</title>
<style>
* {
box-sizing: border-box;
font-family: sans-serif;
}
body {
background-color: #F4F6F9;
}
form,
div {
border-radius: 8px;
padding: 20px;
border: 1px solid #D8DDE6;
color: #16325c;
background-color: #fff;
font-size: 16px;
width: 340px;
position: absolute;
margin-left: calc(-340px / 2);
margin-top: calc(-136px / 2);
left: 50%;
top: 50%;
text-align: center;
}
input, button {
line-height: 100%;
border-radius: 4px;
height: 42px;
}
input {
width: 100%;
border: 1px solid #d8dde6;
padding: 0 14px;
font-size: 14px;
}
button {
border: 0;
margin: 0;
display: block;
width: 100%;
margin-top: 10px;
background-color: #215ca0;
text-transform: uppercase;
color: white;
padding: 0 24px;
font-size: 14px;
cursor: pointer;
}
</style>
</head>
<body>
<form action="%%=CloudPagesURL(1977)=%%" method="post" class="form centered">
<input name="Name" type="text" placeholder="Your name" required>
<button>Send</button>
</form>
<script>
var form = document.querySelector('.form');
if (!!form) {
form.addEventListener("submit",function(e){
e.preventDefault();
if(this.checkValidity()) {
this.submit();
}
});
}
</script>
</body>
</html>
Form Handler
Our goal is to capture the data sent from the Web Form and redirect back to the origin page with a couple of parameters that would allow us to replace the form with a success message.
<!DOCTYPE html>
<html>
<body>
%%[
VAR @Name
SET @Name = RequestParameter("Name")
IF NOT EMPTY(@Name) THEN
Redirect(CONCAT(CloudPagesURL(1970,'Success',1,'Name',@Name)))
ENDIF
]%%
</body>
1970 is the Page ID of the Web Form page.
Now, it is important to add the security measures, so no other domain or bots could send data to our Form Handler.
<!DOCTYPE html>
<html>
<head>
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
</head>
<body>
%%[
SET @origin = HTTPRequestHeader("Referer")
SET @pattern = "^(https:\/\/(.*\.)?((example)\.com))($|\/)"
SET @match = RegExMatch(@origin, @pattern, 1)
]%%
<script runat=server>
Platform.Load("core","1");
var MATCH = Variable.GetValue("@match");
if (!MATCH) { MATCH = null }
HTTPHeader.SetValue("Access-Control-Allow-Methods","POST");
HTTPHeader.SetValue("Access-Control-Allow-Origin",MATCH);
Platform.Response.SetResponseHeader("Strict-Transport-Security","max-age=200");
Platform.Response.SetResponseHeader("X-XSS-Protection","1; mode=block");
Platform.Response.SetResponseHeader("X-Frame-Options","Deny");
Platform.Response.SetResponseHeader("X-Content-Type-Options","nosniff");
Platform.Response.SetResponseHeader("Referrer-Policy","strict-origin-when-cross-origin");
Platform.Response.SetResponseHeader("Content-Security-Policy","default-src 'self'");
</script>
%%[
VAR @Name
IF LENGTH(@match) > 0 THEN
SET @Name = RequestParameter("Name")
IF NOT EMPTY(@Name) THEN
Redirect(CONCAT(CloudPagesURL(1970,'Success',1,'Name',@Name)))
ENDIF
ENDIF
]%%
</body>
That’s it!
Nothing more, nothing less.
You can obviously enhance your Form Handler to write in the Data Extensions, send Emails, retrieve data from SalesForce and so on.
Final code
<!DOCTYPE html>
<html>
<head>
<title>Form</title>
<style>
* {
box-sizing: border-box;
font-family: sans-serif;
}
body {
background-color: #F4F6F9;
}
form,
div {
border-radius: 8px;
padding: 20px;
border: 1px solid #D8DDE6;
color: #16325c;
background-color: #fff;
font-size: 16px;
width: 340px;
position: absolute;
margin-left: calc(-340px / 2);
margin-top: calc(-136px / 2);
left: 50%;
top: 50%;
text-align: center;
}
input, button {
line-height: 100%;
border-radius: 4px;
height: 42px;
}
input {
width: 100%;
border: 1px solid #d8dde6;
padding: 0 14px;
font-size: 14px;
}
button {
border: 0;
margin: 0;
display: block;
width: 100%;
margin-top: 10px;
background-color: #215ca0;
text-transform: uppercase;
color: white;
padding: 0 24px;
font-size: 14px;
cursor: pointer;
}
</style>
</head>
<body>
%%[
VAR @Name
SET @Name = RequestParameter("Name")
SET @Success = RequestParameter("Success")
IF @Success != 1 THEN
]%%
<form action="%%=CloudPagesURL(1977)=%%" method="post" class="form centered">
<input name="Name" type="text" placeholder="Your name" required>
<button>Send</button>
</form>
%%[ ELSE ]%%
<div>
<p>Thank you %%=v(@Name)=%%!</p>
</div>
%%[ ENDIF ]%%
<script>
var form = document.querySelector('.form');
if (!!form) {
form.addEventListener("submit",function(e){
e.preventDefault();
if(this.checkValidity()) {
this.submit();
}
});
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
</head>
<body>
%%[
SET @origin = HTTPRequestHeader("Referer")
SET @pattern = "^(https:\/\/(.*\.)?((example)\.com))($|\/)"
SET @match = RegExMatch(@origin, @pattern, 1)
]%%
<script runat=server>
Platform.Load("core","1");
var MATCH = Variable.GetValue("@match");
if (!MATCH) { MATCH = null }
HTTPHeader.SetValue("Access-Control-Allow-Methods","POST");
HTTPHeader.SetValue("Access-Control-Allow-Origin",MATCH);
Platform.Response.SetResponseHeader("Strict-Transport-Security","max-age=200");
Platform.Response.SetResponseHeader("X-XSS-Protection","1; mode=block");
Platform.Response.SetResponseHeader("X-Frame-Options","Deny");
Platform.Response.SetResponseHeader("X-Content-Type-Options","nosniff");
Platform.Response.SetResponseHeader("Referrer-Policy","strict-origin-when-cross-origin");
Platform.Response.SetResponseHeader("Content-Security-Policy","default-src 'self'");
</script>
%%[
VAR @Name
IF LENGTH(@match) > 0 THEN
SET @Name = RequestParameter("Name")
IF NOT EMPTY(@Name) THEN
Redirect(CONCAT(CloudPagesURL(1970,'Success',1,'Name',@Name)))
ENDIF
ENDIF
]%%
</body>
Have I missed anything?
Please poke me with a sharp comment below or use the contact form.
Thank you for this!
Just to clarify – the actual form is hosted on the website (e.g. WordPress), and the form handler is actually a Cloudpage?
The form can be anywhere you like 😉 Just remember to add some security to your Form Handler.
I tried copying the code into SFMC cloud pages (create landing page > classic editor, one page for form and one for handler). The only change I made to code is changing Page IDs, but on both pages I get the error “500 – Internal server error. There is a problem with the resource you are looking for, and it cannot be displayed.”
Am I doing something wrong?
It seems to me that the IDs you are giving are incorrect. Where do you get them from? They should be from the same Business Unit as the page.
I did have the wrong page ID in the form page code. I fixed it and form page displays and submits, but I am still getting 500 error for handler page. I get page ID from each landing page details info. Both pages are in the same business unit.
(I did add to bottom of handler page even though it wasn’t in the code example)
Could you please tell me what exactly did you add to the code of Form Handler?
Note that, if you don’t use CONCAT in REDIRECT function, it will give Error 500.
Also, did you modify the REGEX at the top? This one: “^(https:\/\/(.*\.)?((example)\.com))($|\/)”
Instead of example.com it should be the domain you are using for Marketing Cloud.
Hi Ivan,
Thanks for the tutorial.
Do I create a cloud pages for the handler ? I tried that but it does not allows me to publish the page handler.
Appreciate your help.
Ivan,
Looks like this failed and stopping me from publishing the form handler.
HTTPHeader.SetValue(“Access-Control-Allow-Origin”, MATCH);
Alan, could you elaborate? Did you get Error 500?
Hi Ivan!
Nice tutorial! Congratulations!
I have a little problem with a implementation that I did. I created a form and a form handler, used my form handler as an action for form, and it worked, but after submission always pops up a error message from MC that says something like “Unable to send data. Please try again”. Perhaps all the processes of the form handler works perfectly. Could you help me fix this problem?
Tks
Dennis, do you use SmartCapture for the form?
No, I don’t use SmartCapture for the form
Ivan, no. I don’t use SmartCapture
Dennis, could you please give me the exact error message and tell me where do you see this error message in MC?
The message is: “pub.s4.exacttarget.com says: Unable to send your data. Please, try again”
Apears a pop up message with this text after click in submission button
Seems like a JavaScript error that appears using “alert”.
Hello Ivan,
I am having an issue implementing the code as well. It is giving me a 500 Error every time I try to view the form handler after publishing.
That’s normal, the form handler is not for viewing. Did you change the id of the page in CloudPagesUrl function?