I have a page that uses amp-list
to dynamically list products using a JSON file. I have my directory structure as follows: *root*/amp/product-name/
and then within the product-name
directory I have an index.html
with the AMP HTML and a products.json
which contains the product data that I display on page. Everything is working as expected if I visit the page directly:
/
The problem es in when I visit from the ampproject CDN.
Here's the actual page, if you visit the console you can see there is a CORS error. How can I handle this? Documentation is not clear on how to handle exactly, just a very vague overview, so for someone new this doesn't make sense to me and I can't just intuit how to do it. I know I need to have a request handler of some sort, which I've partially made by copying the code from AMP's own app.js they link to in their documentation. Within the amp-list
element I previously had the src pointed directly to the JSON file, but now I'm seeing that I need to point to a handler (like a JS file) and then have the handler set the request header and then output the proper JSON.
Here's what I have for the request handler:
/**
* @param {string} url
* @param {string} param
* @param {*} value
* @return {string}
*/
function addQueryParam(url, param, value) {
const paramValue = encodeURIComponent(param) + '=' + encodeURIComponent(value);
if (!url.includes('?')) {
url += '?' + paramValue;
} else {
url += '&' + paramValue;
}
return url;
}
function enableCors(req, res, origin, opt_exposeHeaders) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Origin', '');
res.setHeader('Access-Control-Expose-Headers', ['AMP-Access-Control-Allow-Source-Origin'].concat(opt_exposeHeaders || []).join(', '));
if (req.query.__amp_source_origin) {
res.setHeader('AMP-Access-Control-Allow-Source-Origin', req.query.__amp_source_origin);
}
}
function assertCors(req, res, opt_validMethods, opt_exposeHeaders, opt_ignoreMissingSourceOrigin) {
// Allow disable CORS check (iframe fixtures have origin 'about:srcdoc').
if (req.query.cors == '0') {
return;
}
const validMethods = opt_validMethods || ['GET', 'POST', 'OPTIONS'];
const invalidMethod = req.method + ' method is not allowed. Use POST.';
const invalidOrigin = 'Origin header is invalid.';
const invalidSourceOrigin = '__amp_source_origin parameter is invalid.';
const unauthorized = 'Unauthorized Request';
let origin;
if (validMethods.indexOf(req.method) == -1) {
res.statusCode = 405;
res.end(JSON.stringify({
message: invalidMethod
}));
throw invalidMethod;
}
if (req.headers.origin) {
origin = req.headers.origin;
if (!ORIGIN_REGEX.test(req.headers.origin)) {
res.statusCode = 500;
res.end(JSON.stringify({
message: invalidOrigin
}));
throw invalidOrigin;
}
if (!opt_ignoreMissingSourceOrigin && !SOURCE_ORIGIN_REGEX.test(req.query.__amp_source_origin)) {
res.statusCode = 500;
res.end(JSON.stringify({
message: invalidSourceOrigin
}));
throw invalidSourceOrigin;
}
} else if (req.headers['amp-same-origin'] == 'true') {
origin = getUrlPrefix(req);
} else {
res.statusCode = 401;
res.end(JSON.stringify({
message: unauthorized
}));
throw unauthorized;
}
enableCors(req, res, origin, opt_exposeHeaders);
}
And here's what I'm using for the HTML.
<amp-list credentials="include" width="auto" height="1500" layout="fixed-height" src=".js" class="m1">
<template type="amp-mustache" id="amp-template-id">
<div class="product" style="padding-top: 1em;">
<a class="wrapper-link" href="{{ link }}">
<amp-img alt="{{ title }}" width="1000" height="1000" src="{{ src }}" layout="responsive"></amp-img>
<h3 class="product-name text-centered">
{{title}}
</h3>
<h4 class="text-centered sku margin-minus-1">{{ sku }}</h4>
<div class="text-centered get-price">
<a class="text-centered get-price-link" href="{{ link }}">Get pricing now</a>
</div>
</a>
</div>
</template>
<div overflow role="button" aria-label="Show more">
Show more
</div>
</amp-list>
.perfectimprints/amp/trick-or-treat-bags/index.html
I have a page that uses amp-list
to dynamically list products using a JSON file. I have my directory structure as follows: *root*/amp/product-name/
and then within the product-name
directory I have an index.html
with the AMP HTML and a products.json
which contains the product data that I display on page. Everything is working as expected if I visit the page directly:
https://example./amp/product-name/
The problem es in when I visit from the ampproject CDN.
Here's the actual page, if you visit the console you can see there is a CORS error. How can I handle this? Documentation is not clear on how to handle exactly, just a very vague overview, so for someone new this doesn't make sense to me and I can't just intuit how to do it. I know I need to have a request handler of some sort, which I've partially made by copying the code from AMP's own app.js they link to in their documentation. Within the amp-list
element I previously had the src pointed directly to the JSON file, but now I'm seeing that I need to point to a handler (like a JS file) and then have the handler set the request header and then output the proper JSON.
Here's what I have for the request handler:
/**
* @param {string} url
* @param {string} param
* @param {*} value
* @return {string}
*/
function addQueryParam(url, param, value) {
const paramValue = encodeURIComponent(param) + '=' + encodeURIComponent(value);
if (!url.includes('?')) {
url += '?' + paramValue;
} else {
url += '&' + paramValue;
}
return url;
}
function enableCors(req, res, origin, opt_exposeHeaders) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Origin', 'https://www-perfectimprints-.cdn.ampproject');
res.setHeader('Access-Control-Expose-Headers', ['AMP-Access-Control-Allow-Source-Origin'].concat(opt_exposeHeaders || []).join(', '));
if (req.query.__amp_source_origin) {
res.setHeader('AMP-Access-Control-Allow-Source-Origin', req.query.__amp_source_origin);
}
}
function assertCors(req, res, opt_validMethods, opt_exposeHeaders, opt_ignoreMissingSourceOrigin) {
// Allow disable CORS check (iframe fixtures have origin 'about:srcdoc').
if (req.query.cors == '0') {
return;
}
const validMethods = opt_validMethods || ['GET', 'POST', 'OPTIONS'];
const invalidMethod = req.method + ' method is not allowed. Use POST.';
const invalidOrigin = 'Origin header is invalid.';
const invalidSourceOrigin = '__amp_source_origin parameter is invalid.';
const unauthorized = 'Unauthorized Request';
let origin;
if (validMethods.indexOf(req.method) == -1) {
res.statusCode = 405;
res.end(JSON.stringify({
message: invalidMethod
}));
throw invalidMethod;
}
if (req.headers.origin) {
origin = req.headers.origin;
if (!ORIGIN_REGEX.test(req.headers.origin)) {
res.statusCode = 500;
res.end(JSON.stringify({
message: invalidOrigin
}));
throw invalidOrigin;
}
if (!opt_ignoreMissingSourceOrigin && !SOURCE_ORIGIN_REGEX.test(req.query.__amp_source_origin)) {
res.statusCode = 500;
res.end(JSON.stringify({
message: invalidSourceOrigin
}));
throw invalidSourceOrigin;
}
} else if (req.headers['amp-same-origin'] == 'true') {
origin = getUrlPrefix(req);
} else {
res.statusCode = 401;
res.end(JSON.stringify({
message: unauthorized
}));
throw unauthorized;
}
enableCors(req, res, origin, opt_exposeHeaders);
}
And here's what I'm using for the HTML.
<amp-list credentials="include" width="auto" height="1500" layout="fixed-height" src="https://perfectimprints./amp/handle-amp-requests.js" class="m1">
<template type="amp-mustache" id="amp-template-id">
<div class="product" style="padding-top: 1em;">
<a class="wrapper-link" href="{{ link }}">
<amp-img alt="{{ title }}" width="1000" height="1000" src="{{ src }}" layout="responsive"></amp-img>
<h3 class="product-name text-centered">
{{title}}
</h3>
<h4 class="text-centered sku margin-minus-1">{{ sku }}</h4>
<div class="text-centered get-price">
<a class="text-centered get-price-link" href="{{ link }}">Get pricing now</a>
</div>
</a>
</div>
</template>
<div overflow role="button" aria-label="Show more">
Show more
</div>
</amp-list>
https://www-perfectimprints-.cdn.ampproject/c/s/www.perfectimprints./amp/trick-or-treat-bags/index.html
Share Improve this question asked Aug 24, 2018 at 18:04 PatrickPatrick 3745 silver badges22 bronze badges 2- The src in amp-list is only intended for the JSON source file that contains the info for your mustache tags, not JS as you currently have set. Your CORS headers need to be set in the page header information. Best bet, depending on your server setup (Apache or Nginx), is to set your CORS headers up in your htacess (Apache) or conf (Nginx) file. – Craig Scott Commented Aug 24, 2018 at 18:20
- Okay, how would you do that? I've tried that way using .htaccess but it's not working. – Patrick Commented Aug 24, 2018 at 18:22
3 Answers
Reset to default 5This was the solution that worked for me.
Header set Access-Control-Allow-Credentials "true"
Header set Access-Control-Allow-Origin "https://example-.cdn.ampproject"
Header set Access-Control-Allow-Source-Origin "https://example."
Header set Access-Control-Expose-Headers AMP-Access-Control-Allow-Source-Origin
Header set AMP-Access-Control-Allow-Source-Origin "https://example."
Thank you to @Craig Scott for the prompt to help me solve.
Woe be the poor, inexperienced soul who deals with AMP pages.
Presuming you're on an Apache server, you could set your CORS header in your htaccess file like this:
Header set Access-Control-Allow-Origin "https://www-perfectimprints-.cdn.ampproject"
Header set Access-Control-Allow-Origin "https://www-perfectimprints-.amp.cloudflare."
Then ensure your htaccess file is uploaded to the directory that contains your website HTML. That's how it's set-up on the site I manage and everything works for us.
For people using nodejs, here's the syntax:
res.set('AMP-Redirect-To', encodedUrl);
res.set('Access-Control-Allow-Origin', websiteHostname);
res.set('AMP-Access-Control-Allow-Source-Origin', websiteHostname);
res.set('Access-Control-Expose-Headers', [
'AMP-Access-Control-Allow-Source-Origin',
'AMP-Redirect-To',
]);
res.set('Access-Control-Allow-Credentials', 'true');
ِAnother solution is to use the amp-toolbox-cors
library and set it's verifyOrigin
option to false
.
import ampCors from 'amp-toolbox-cors';
app.use(
ampCors({
verifyOrigin: false,
}),
);
beware that all origins are allowed like that. In reality you should allow just two.
Hope it saves somebody's time in the future!