CORS in AMP for Email
Important: this documentation is not applicable to your currently selected format websites!
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 as- AMP-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 as- Originin the request
- AMP-Access-Control-Allow-Source-Originwith the same value as the- __amp_source_originquery string parameter in the request.
- Access-Control-Expose-Headersset to- AMP-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.
-