You are currently viewing Safe Online Transactions on AMP: A Developer’s Security Guide

Safe Online Transactions on AMP: A Developer’s Security Guide

When a user taps a link to a product page from a Google search result, the page loads in under a second thanks to Accelerated Mobile Pages (AMP). That speed comes from a stripped-down HTML framework and a mandatory CDN cache. But if that page accepts payments—a donation, a subscription, or a one-time purchase—the same constraints that make AMP fast also introduce unique security pitfalls. A missing amp-form attribute, a relaxed Content Security Policy, or an unsigned AMP component can turn a lightning‑fast checkout into a data leak.

developer inspecting AMP payment form source code

Understanding AMP and Its Role in Online Transactions

AMP is not a framework you can bolt onto an existing page. It enforces a strict set of HTML tags, a single JavaScript library, and a cache layer (the AMP Cache) that serves a pre‑validated copy of your page. For transaction pages this means:

  • All JavaScript must be asynchronous and cannot be authored by the developer — only the AMP runtime and a whitelist of extension scripts are allowed.
  • Forms must use the amp-form extension, which restricts redirects and custom validation logic.
  • User‑generated content is heavily restricted; dynamic price updates require the amp-bind component with careful state management.

These restrictions reduce the attack surface for XSS and injection attacks, but they also shift responsibility to the server side and to the AMP validation pipeline. A developer must ensure that the AMP page itself is cryptographically signed (if using AMP for Email or AMP for Transactions) and that the origin server enforces proper authorization before processing the payment.

Common Security Risks in AMP Transactions

Because AMP pages are cached and served from a different domain (e.g., example-com.cdn.ampproject.org), the browser’s Same‑Origin Policy treats the cached page as coming from the CDN, not from your domain. This has direct consequences for transactions:

  1. Session cookie leakage: Cookies set for example.com are not sent to the AMP cache domain. Any payment flow that relies on session cookies will break unless you implement an alternative token mechanism (e.g., a signed URL or a one‑time token in the page’s amp-state).
  2. Phishing via cache: An attacker who compromises an AMP component (or uses a malicious AMP ad) could inject a fake payment form into a cached page. The AMP runtime does verify the integrity of the page against the origin’s signed exchange, but only if you serve an application/signed-exchange response. Without SXG, the cache is a plain copy.
  3. Form action spoofing: The action attribute in <form method=post action-xhr> must be HTTPS and must point to an endpoint that validates the AMP-Same-Origin header. If an attacker can modify the cached response (e.g., via a compromised CDN edge), they can redirect the form submission to their own server.

These risks are not theoretical. In 2023 a major e‑commerce platform discovered that its AMP checkout pages were not validating the __amp_source_origin header, allowing cross‑origin form submissions to leak credit card data. The fix was a server‑side check that rejects any POST request missing that header.

Implementing HTTPS and Content Security Policy

AMP requires all resources to be served over HTTPS. That includes images, fonts, and especially the action-xhr endpoint. But HTTPS alone is not enough. You must also set a strict Content Security Policy (CSP) that whitelists only the AMP CDN and your own origin. A typical CSP for an AMP payment page looks like this:

Content-Security-Policy: default-src 'self' ; script-src 'self'  'unsafe-inline'; style-src 'self'  'unsafe-inline'; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'self' ;

Note the form-action directive: it restricts where form data can be submitted. If your payment gateway is a third‑party API, you must explicitly list its HTTPS endpoint. Never use form-action * on a transaction page.

Additionally, set the X-Frame-Options: DENY header to prevent clickjacking, and include Referrer-Policy: no-referrer to avoid leaking the page URL to external analytics scripts. AMP’s own amp-iframe component is sandboxed by default, but you should still avoid embedding payment iframes from untrusted sources.

Validating AMP Signatures and Caching

To guarantee that the cached page is identical to the one you published, use Signed Exchanges (SXG). An SXG is a cryptographic envelope that proves the page came from your origin, even when served from a cache. The AMP Cache will verify the signature before delivering the page. For transaction pages, this is critical because it prevents cache poisoning by an attacker who might have write access to the CDN edge.

To implement SXG, you need a certificate that supports the CanSignHttpExchanges extension (available from DigiCert and Google Trust Services). Your server must generate the signed exchange at build time or on the fly. The amp-install-serviceworker script can then prefetch the signed version. Without SXG, consider adding a Link header with rel=preload for the AMP runtime to reduce the window for cache tampering.

Another safeguard is to validate the AMP-Cache-Transform header on your origin server. When the AMP Cache forwards a request to your server (for dynamic content), it includes this header. Reject any request that lacks it, and respond with a Cache-Control: private directive for sensitive endpoints.

Securing Payment Forms in AMP

The amp-form extension provides built‑in protections: it enforces HTTPS, prevents cross‑origin form submission unless the allow-cross-domain attribute is set, and automatically includes the __amp_source_origin parameter in POST requests. On the server side, always verify this parameter matches your own origin. Here is a minimal secure form:

<form method=post action-xhr="; target=_top>
  <input type=hidden name="__amp_source_origin" value=";
  <input type=text name="card-number" placeholder="Card number" required>
  <input type=submit value="Pay">
</form>

Never include sensitive fields like CVV or full card numbers in the HTML source. Instead, use a tokenization service (e.g., Stripe Elements) that generates a one‑time token via an amp-iframe or a separate page. The AMP runtime cannot execute arbitrary JavaScript, so you must rely on the payment gateway’s own iframe or a redirect flow.

For subscription models, store only a hashed reference to the payment method on the AMP page. The actual charge should happen server‑side after the user submits the form. Use the amp-list component to display a confirmation message only after the server returns a success response with a 200 status and a Content-Type: application/json.

Testing Your AMP Transaction Flow

Before going live, run the AMP validator on every transaction page. The command‑line amphtml-validator tool catches missing required tags and disallowed attributes. Then test the full flow using a staging environment that mimics the AMP Cache:

  • Serve your page with ?amp_js_v=0 to disable the AMP runtime and see the raw HTML.
  • Use curl to simulate a POST to your action-xhr endpoint with a spoofed __amp_source_origin — your server should reject it.
  • Check that the cache URL (e.g., ) renders the same form and that the submit button works.
  • Enable X-Content-Type-Options: nosniff and Strict-Transport-Security headers on the origin server.

One often‑overlooked detail: the AMP Cache may serve a stale version of your page for up to 5 minutes. If you push a security fix (e.g., updating a form action URL), you must invalidate the cache by sending a Content-Location header with the new URL or by using the amp-cache-invalidate API. For high‑value transactions, consider setting a short cache lifetime (max-age=10) on the AMP page itself, even though it slightly reduces performance.

For a deeper look at how AMP link previews interact with security headers, the blog post New WhatsApp Update: AMP Link Previews and What Developers Need to Know covers similar validation patterns that apply to any platform rendering AMP pages.

Finally, audit your payment endpoint’s logs for any POST requests that arrive without the AMP-Same-Origin header or with a mismatched __amp_source_origin. Those are early indicators of a cross‑site request forgery attempt. Automate a daily check that alerts you if any such request succeeds. That single metric, combined with the structural safeguards above, keeps AMP transactions fast without sacrificing security.