CORS in AMP for Email
Important: this documentation is not applicable to your currently selected format ads!
Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to tell browsers which origins are allowed access to a resource using XHR. AMP for Email extends this mechanism by adding HTTP headers that similarly tell email clients which senders are allowed access to these resources.
There are currently two versions of this mechanism. For the time being, it's recommended to support both on your server-side.
Version 2
When an email makes a request via amp-form, amp-list or any other XHR-based mechanism, the email client includes the following HTTP header:
AMP-Email-Sender, set to the email address of the sender of the email.
It expects that the HTTP response contains the following header in return:
AMP-Email-Allow-Senderwith either the same value asAMP-Email-Senderin the request, or*indicating all sender emails are allowed.
Example
A user opens an email from sender@company.example in their email client by going to https://emailclient.example/myinbox. The email uses AMP for Email and loads data using an amp-list from https://company.example/data.json.
The email client sends an HTTP request to https://company.example/data.json with the following headers set:
AMP-Email-Sender: sender@company.example
The email client expects that the HTTP response contains the following headers:
AMP-Email-Allow-Sender: sender@company.example
Alternatively, using * in either header is allowed (but not recommended).
Version 1 (deprecated)
In version 1, the email client uses a query string parameter instead of an HTTP header to indicate the sender email. It also provides an Origin header and requires the Access-Control-Allow-Origin header in response, like CORS on websites.
When an email makes a request via amp-form, amp-list or any other XHR-based mechanism, the email client includes the following HTTP header:
Originwith the value of the origin of the page used to display the email.
The URL also always has a query string with the __amp_source_origin parameter set to the email address of the sender of the email.
It expects that the HTTP response contains the following headers:
Access-Control-Allow-Originwith the same value asOriginin the requestAMP-Access-Control-Allow-Source-Originwith the same value as the__amp_source_originquery string parameter in the request.Access-Control-Expose-Headersset toAMP-Access-Control-Allow-Source-Origin
Example
A user opens an email from sender@company.example in their email client by going to https://emailclient.example/myinbox. The email uses AMP for Email and loads data using an amp-list from https://company.example/data.json.
The email client sends an HTTP request to https://company.example/data.json?__amp_source_origin=sender@company.example (notice the added query string) with the following header set:
Origin: https://emailclient.example
The email client expects that the HTTP response contains the following headers:
Access-Control-Allow-Origin: https://emailclient.example
AMP-Access-Control-Allow-Source-Origin: sender@company.example
Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin
Note that this version does not support using * in the AMP-Access-Control-Allow-Source-Origin header.
Implementing CORS
These are the recommended steps to take on the server-side to implement CORS that supports both version 1 and 2:
When you receive an HTTP request, check if the Origin and AMP-Email-Sender HTTP headers are set.
- If the
AMP-Email-Senderheader is set:- Let senderEmail be the value of the
AMP-Email-Senderheader. - Check if senderEmail is an email address owned by you or one that you trust. If not, reject the request.
- Set the response header
AMP-Email-Allow-Senderto senderEmail.
- Let senderEmail be the value of the
- If the
Originheader is set, butAMP-Email-Senderis not set:- Let requestOrigin be the value of the
Originheader. - Set the response header
Access-Control-Allow-Originto requestOrigin. - Check if the URL contains the
__amp_source_originquery string parameter. If not reject the request. - Let senderEmail be the value of the
__amp_source_originquery string parameter. - Check if senderEmail is an email address owned by you or one that you trust. If not, reject the request.
- Set the response header
AMP-Access-Control-Allow-Source-Originto senderEmail. - Set the response header
Access-Control-Expose-HeaderstoAMP-Access-Control-Allow-Source-Origin.
- Let requestOrigin be the value of the
- If neither
OriginnorAMP-Email-Senderare set, reject the request.
Example 1
Request sent by email client
GET /data.json?__amp_source_origin=sender@company.example HTTP/1.1
Host: company.example
Origin: https://emailclient.example
User-Agent: EmailClientProxy
Accept: application/json
Response headers expected
Access-Control-Allow-Origin: https://emailclient.example
Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin
AMP-Access-Control-Allow-Source-Origin: sender@company.example
Explanation
Because the Origin header was set, this request is using CORS version 1 and requires the three headers listed above to be set.
Example 2
Request sent by email client
GET /data.json HTTP/1.1
Host: company.example
AMP-Email-Sender: sender@company.example
User-Agent: EmailClientProxy
Accept: application/json
Response headers expected
AMP-Email-Allow-Sender: sender@company.example
Explanation
Because the AMP-Email-Sender header was set, this request is using CORS version 2 and only requires the AMP-Email-Allow-Sender header.
Example 3
Request sent by email client
GET /data.json?__amp_source_origin=sender@company.example HTTP/1.1
Host: company.example
Origin: https://emailclient.example
AMP-Email-Sender: sender@company.example
User-Agent: EmailClientProxy
Accept: application/json
Response headers expected
AMP-Email-Allow-Sender: sender@company.example
Explanation
Both Origin and AMP-Email-Sender are set, indicating that the client supports both versions. Because version 2 takes precedence, only the AMP-Email-Allow-Sender header is set and Origin and the value of __amp_source_origin can be safely ignored.
Code examples
PHP
if (isset($_SERVER['HTTP_AMP_EMAIL_SENDER'])) {
$senderEmail = $_SERVER['HTTP_AMP_EMAIL_SENDER'];
if (!isAllowedSender($senderEmail)) {
die('invalid sender');
}
header("AMP-Email-Allow-Sender: $senderEmail");
} elseif (isset($_SERVER['HTTP_ORIGIN'])) {
$requestOrigin = $_SERVER['HTTP_ORIGIN'];
if (empty($_GET['__amp_source_origin'])) {
die('invalid request');
}
$senderEmail = $_GET['__amp_source_origin'];
if (!isAllowedSender($senderEmail)) {
die('invalid sender');
}
header("Access-Control-Allow-Origin: $requestOrigin");
header('Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin');
header("AMP-Access-Control-Allow-Source-Origin: $senderEmail");
} else {
die('invalid request');
}
Python (Django)
response = JsonResponse(...)
if request.META.HTTP_AMP_EMAIL_SENDER:
senderEmail = request.META.HTTP_AMP_EMAIL_SENDER
if not isAllowedSender(senderEmail):
raise PermissionDenied
response['AMP-Email-Allow-Sender'] = senderEmail
elif request.META.HTTP_ORIGIN:
requestOrigin = request.META.HTTP_ORIGIN
senderEmail = request.GET.get('__amp_source_origin')
if not isAllowedSender(senderEmail):
raise PermissionDenied
response['Access-Control-Allow-Origin'] = requestOrigin
response['Access-Control-Expose-Headers'] = 'AMP-Access-Control-Allow-Source-Origin'
response['AMP-Access-Control-Allow-Source-Origin'] = senderEmail
else
raise PermissionDenied
SSJS
<script runat="server" language="JavaScript">
Platform.Load("core", "1");
if (Platform.Request.GetRequestHeader("AMP-Email-Sender")) {
var senderEmail = Platform.Request.GetRequestHeader("AMP-Email-Sender")
if (isValidSender(senderEmail)) {
HTTPHeader.SetValue("AMP-Email-Allow-Sender", senderEmail)
} else {
Platform.Function.RaiseError("Sender Not Allowed",true,"statusCode","3");
}
} else if (Platform.Request.GetRequestHeader("Origin")) {
var requestOrigin = Platform.Request.GetRequestHeader("Origin")
if (Platform.Request.GetQueryStringParameter("__amp_source_origin")) {
var senderEmail = Platform.Request.GetQueryStringParameter("__amp_source_origin");
if (isValidSender(senderEmail)) {
HTTPHeader.SetValue("Access-Control-Allow-Origin", requestOrigin);
HTTPHeader.SetValue("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin");
HTTPHeader.SetValue("AMP-Access-Control-Allow-Source-Origin", senderEmail);
} else {
Platform.Function.RaiseError("Invalid Source Origin",true,"statusCode","3");
}
} else {
Platform.Function.RaiseError("Source Origin Not Present",true,"statusCode","3");
}
} else {
Platform.Function.RaiseError("Origin and Sender Not Present",true,"statusCode","3");
}
</script>
Visit Salesforce Developer Documentation to learn more about SSJS.
Node.js
Use the officially supported @ampproject/toolbox-cors npm package.
-
Written by @fstanis