Title: AMP WP &#8211; Google AMP For WordPress
Author: Pixelative
Published: <strong>September 17, 2018</strong>
Last modified: May 25, 2026

---

Search plugins

![](https://ps.w.org/amp-wp/assets/banner-772x250.png?rev=1946984)

![](https://ps.w.org/amp-wp/assets/icon-256x256.png?rev=2552940)

# AMP WP – Google AMP For WordPress

 By [Pixelative](https://profiles.wordpress.org/pixelative/)

[Download](https://downloads.wordpress.org/plugin/amp-wp.1.7.11.zip)

 * [Details](https://mfe.wordpress.org/plugins/amp-wp/#description)
 * [Reviews](https://mfe.wordpress.org/plugins/amp-wp/#reviews)
 * [Development](https://mfe.wordpress.org/plugins/amp-wp/#developers)

 [Support](https://wordpress.org/support/plugin/amp-wp/)

## Description

**AMP WP** is the most feature-rich, developer-friendly, and beginner-ready Google
AMP plugin available for WordPress, completely free.

Originally built by **[Pixelative](https://pixelative.co)**, a professional web 
development agency, AMP WP is now independently developed and maintained by **[Mohsin Rafique](https://mohsinrafique.com)**,
the lead engineer behind the plugin since its inception. AMP WP has helped thousands
of website owners dramatically improve mobile page speed, search rankings, and user
experience.

[Online Demo](https://pixelspress.com/amp) | [CF7 Premium Extension](https://1c8979da.sibforms.com/serve/MUIFAPfaW_zG5aVJNBG78I1qIu30utV8kT5_ERiosqVMstRk9Nj1o6RVbEbyaJkvyZ9TQ3b79YDkpPjsyd99NmbxAlNtZQrx9TkcFAF3V6DvgcoiCcnaZkgvPxl_OpctNUzI-G7xDKxwMq2d2Cm2RteW4LoOxEi-XMmdvhfCiMDxfBsOx18Pd9U1xsTcOqIdyT9WccheL83rrsCeeQ==)
| [Support](https://mfe.wordpress.org/plugins/amp-wp/mohsin.rafique@gmail.com?output_format=md)

### Why AMP WP?

Google’s Accelerated Mobile Pages (AMP) technology delivers pages up to **5x faster**
on mobile devices. Faster pages mean lower bounce rates, higher engagement, and 
better rankings in Google Search. AMP WP makes enabling AMP on your WordPress site
effortless, from a single settings panel with no theme modifications needed.

### Free Features

**Performance & Compatibility**
 * Full Google AMP specification compliance (100%
valid AMP output) * PHP 8.x compatible (tested through PHP 8.4) * Compatible with
all major caching plugins (WP Rocket, SG Optimizer, W3 Total Cache, and more) * 
Compatible with Jetpack * Compatible with Yoast SEO (100%) * Compatible with “Automattic
AMP” plugin * Gutenberg block editor support * RTL language support (100%) * GDPR
compliant

**Content & Embeds**
 * Automatic AMP conversion for Posts, Pages, and Custom Post
Types * Embed Images, Videos, Audios, and iFrames: YouTube, Vimeo, Twitter, Facebook,
SoundCloud, Instagram (Posts, Reels & TV) * Lightbox for Images * Slider Support*
Internal AMP Linking * Native AMP Search

**Design & Customization**
 * Two listing layouts: Classic View and List View * 
Customizable color scheme * Custom CSS options * Sticky Header * AMP WordPress Navigation
Menu * Custom AMP Front Page * Social Icons

**Control**
 * Show/Hide AMP for individual Posts, Pages, and Custom Post Types *
Show/Hide AMP for Taxonomies (Categories, Tags, Custom) * Show/Hide AMP mobile redirections*
Show/Hide AMP on Search pages * Show/Hide Date, Author, and Thumbnail across Archive,
Single Post, Related Posts, and Slider * Show/Hide Tags on Single Post pages * Enable/
Disable Structured Data (JSON-LD Schema)

**Engagement & Analytics**
 * Related Posts (with thumbnail, date, and author controls)*
Recent Comments * Notice Bar * Sidebar * Social Sharing Buttons * OneSignal Push
Notifications integration * Third-party analytics support: Google Analytics, Facebook
Pixel, Segment, Quantcast, Alexa Metrics, Chartbeat, comScore, Yandex Metrica, AFS
Analytics, Adobe Analytics

**Ads**
 * Google AdSense Auto Ads for AMP support

**Multilingual**
 * Translation Panel included * Contact us at `mohsin.rafique@gmail.
com` to contribute translations

### Maintained by Mohsin Rafique

AMP WP was originally created by **[Pixelative](https://pixelative.co)**, a full-
service digital agency specializing in high-performance WordPress solutions, and
remains under Pixelative’s ownership. The plugin is currently developed, updated,
and supported independently by **Mohsin Rafique**, a seasoned WordPress engineer
with deep expertise in AMP specification compliance, PHP performance, and modern
web standards.

For support, custom AMP theme development, or white-label licensing, contact **Mohsin
Rafique** at [mohsinrafique.com](https://mohsinrafique.com) or [mohsin.rafique@gmail.com](https://mfe.wordpress.org/plugins/amp-wp/mohsin.rafique@gmail.com?output_format=md).

#### Credits

 * [WordPress Plugin Boilerplate Generator](https://wppb.me/) by Tom McFarlin, License:
   GPLv2
 * [normalize.css](http://necolas.github.io/normalize.css/), License: MIT
 * [Karla](https://fonts.google.com/specimen/Karla) by Jonathan Pinhorn, License:
   SIL OFL
 * [Noto Sans](https://fonts.google.com/specimen/Noto+Sans) by Google, License: 
   SIL OFL
 * [Overpass Mono](https://fonts.google.com/specimen/Overpass+Mono) by Delve Withrington,
   License: SIL OFL
 * [Font Awesome Free 6.7.2](https://fontawesome.com/license) by Fonticons, Inc.,
   Font: SIL OFL 1.1 / Code: MIT

### Minimum Requirements

 * WordPress 5.0 or higher
 * PHP 7.4 or higher
 * MySQL 8.0+ or MariaDB 10.5+

**Tip:** Install [Regenerate Thumbnails](https://wordpress.org/plugins/regenerate-thumbnails/)
and regenerate your thumbnails after activation to ensure the post listing layout
renders correctly.

### Automatic Installation (Recommended)

 1. Log in to your WordPress admin panel.
 2. Navigate to **Plugins -> Add New**.
 3. Search for **AMP WP**.
 4. Click **Install Now**, then **Activate**.
 5. Go to **AMP WP -> Customize AMP** to configure your settings.

### Manual Installation

 1. Download the plugin ZIP from the WordPress plugin repository.
 2. Extract the ZIP and upload the `amp-wp` folder to `/wp-content/plugins/` via FTP
    or your hosting file manager.
 3. Activate the plugin from **Plugins** in your WordPress admin panel.
 4. Navigate to **AMP WP -> Customize AMP** to begin setup.

### Developer Hooks

AMP WP exposes WordPress-standard actions and filters throughout its template system,
URL generator, structured data engine, and admin panel. All hooks listed here are
safe to use in a child theme’s `functions.php` or in a standalone plugin. No plugin
file edits are needed.

#### Template Actions

These actions fire inside AMP WP’s own template system. They do not use `wp_head`
or `wp_footer`. Output must be valid AMP markup.

**`amp_wp_template_head`**
 Fires inside the AMP `<head>` element. Use for custom
meta tags or other head markup. AMP WP reserves priority 0 for component scripts.

**`amp_wp_template_head_deferred`**
 Fires in `<head>` after the AMP boilerplate
styles have been written. Required injection point for `<amp-auto-ads>` and any 
snippet that must follow the boilerplate. AMP WP’s own styles and scripts output
here.

**`amp_wp_template_body_start`**
 Fires immediately after the opening `<body>` tag.

**`amp_wp_body_beginning`**
 Fires at the start of visible body content, before 
the site header renders.

**`amp_wp_post_content_below`**
 Fires directly below the post content on single
post pages.

**`amp_wp_template_footer`**
 Fires before the closing `</body>` tag. Priority 999
is reserved for AMP WP’s output sanitizer.

**`amp_wp_template_enqueue_scripts`**
 Use to register additional AMP component 
scripts. Call `amp_wp_enqueue_script( $handle, $src )` inside your callback.

**`amp_wp_after_comment_list`**
 Fires after the comment list on single post pages.

**`amp_wp_notifications_bar`**
 Fires inside the notification/notice bar template
slot.

**`amp_wp_gdpr_compliance`**
 Fires inside the GDPR banner template slot.

#### Analytics Actions

These actions fire in the AMP page footer. Each corresponds to one analytics provider
already supported by AMP WP. Hook into any of them to append a custom `<amp-analytics
>` block for a provider not yet built in, or to modify the existing output before
it renders.

 * `amp_wp_analytics_ga` (Google Analytics)
 * `amp_wp_analytics_fbp` (Facebook Pixel)
 * `amp_wp_analytics_sa` (Simple Analytics)
 * `amp_wp_analytics_qc` (Quantcast)
 * `amp_wp_analytics_acm` (Adobe Campaign Manager)
 * `amp_wp_analytics_cb` (Chartbeat)
 * `amp_wp_analytics_comscore` (comScore)
 * `amp_wp_analytics_yandex_metrica` (Yandex Metrica)
 * `amp_wp_analytics_afs` (AFS Analytics)
 * `amp_wp_analytics_adobe` (Adobe Analytics)

#### AMP Version Control Filters

**`amp_wp_amp_version_exists`**
 Control whether an AMP version is served for the
current request. Return `false` to disable AMP for that page. Parameter: `(bool)
$exists`

    ```
    add_filter( 'amp_wp_amp_version_exists', '__return_false' );
    ```

**`amp_wp_template_auto_redirect`**
 Enable automatic redirection of all visitors
from the non-AMP URL to the AMP URL. Default `false`. Parameter: `(bool) $redirect`

**`amp_wp_filter_config_list`**
 Extend the list of post types, taxonomies, and 
conditions where AMP is disabled site-wide. Parameter: `(array) $filters`

#### URL and Permalink Filters

**`amp_wp_pre_get_permalink`**
 Short-circuit AMP permalink generation. Return any
non-false value to bypass the core URL builder. Parameters: `(mixed) $pre` (default
false), `(int) $post_id`

**`amp_wp_get_permalink`**
 Filter the final AMP URL after it has been built. Parameters:`(
string) $amp_url`, `(int) $post_id`

**`amp_wp_url_format_filter`**
 Change the AMP URL format. Accepted values: `start-
point` (prefix, e.g. `/amp/slug`) or `end-point` (suffix, e.g. `/slug/amp`). Parameter:`(
string) $format`

**`amp_wp_url_excluded`**
 Add URL paths that must never serve an AMP version. Parameter:`(
array) $excluded_urls`

**`amp_wp_transformer_exclude_subdir`**
 Add path segments that the AMP URL transformer
should skip when rewriting internal links. Used internally by AMP WP for WPML and
Polylang language prefixes. Parameter: `(array) $exclude_dirs`

#### Structured Data Filters (JSON-LD)

All JSON-LD filters are active only when “Enable Structured Data on Site” is on 
in AMP WP Settings. The three schema-data filters carry a trailing underscore. This
is generated internally via `sprintf( 'amp_wp_json_ld_%s_', $type )` and the underscore
must be part of the hook name you register.

**`amp_wp_json_ld_config`**
 Filters the generator configuration before any schema
is built.

Parameter: `(array) $config` with keys:
 * `active` (bool): set `false` to disable
the entire JSON-LD generator for the current request. * `logo` (string): Organization
logo URL. When non-empty, an `Organization` block is emitted on every page and all
Article-type schemas reference it by `@id`. * `posts_type` (string): Fallback schema
type when `schema_type_for_post` is empty. Default `BlogPosting`. * `media_field_id`(
string): Post meta key for audio/video URL in format posts. Default `_featured_embed_code`.

AMP WP core attaches at priority 15 to inject the branding logo. Use priority 20
or higher to override it.

    ```
    add_filter( 'amp_wp_json_ld_config', function( $config ) { ... }, 20 );
    ```

**`amp_wp_json_ld_organization_`**
 Filters the `Organization` schema array before
output. Fires only when a logo is configured. Return `false` or an empty array to
suppress the block. Parameter: `(array) $data`

    ```
    add_filter( 'amp_wp_json_ld_organization_', function( $data ) { ... } );
    ```

**`amp_wp_json_ld_website_`**
 Filters the `WebSite` schema array before output.
Fires only on the homepage and front page. Parameter: `(array) $data`

    ```
    add_filter( 'amp_wp_json_ld_website_', function( $data ) { ... } );
    ```

**`amp_wp_json_ld_single_`**
 Filters the singular-content schema array before output.
Fires on all singular pages: standard posts (Article, NewsArticle, BlogPosting),
static pages (WebPage), WooCommerce products (Product), post format overrides (AudioObject,
VideoObject, ImageObject), and custom post types. Parameter: `(array) $data`

    ```
    add_filter( 'amp_wp_json_ld_single_', function( $data ) { ... } );
    ```

#### Content and Theme Filters

**`amp_wp_template_page_on_front`**
 Specify the page ID to use as the AMP front
page when a static homepage is configured. Parameter: `(int) $page_id`, default 
0.

**`amp_wp_template_show_on_front`**
 Control what displays on the AMP homepage. 
Accepted values: `posts` or `page`. Parameter: `(string) $show_on_front`

**`amp_wp_template_active_template`**
 Override the active template metadata array.
Parameter: `(array) $template_info`

**`amp_wp_template_set_menu_walker`**
 Enable or disable the custom sidebar menu
walker for a specific nav menu location. Parameters: `(bool) $use_walker`, `(array)
$args`

**`amp-wp-template-default-theme-mod`**
 Set default values for AMP WP Customizer
options. Parameters: `(mixed) $default_value`, `(string) $option_key`

**`amp_wp_home_featured`**
 Customize the `WP_Query` arguments used to fetch posts
for the homepage featured slider. Parameter: `(array) $query_args`

**`amp_wp_get_template`**
 Override the file path used to locate a template part.
Parameters: `(string) $located`, `(string) $file`, `(array) $args`, `(string) $template_path`,`(
string) $default_path`

**`amp_wp_html_dom_filter_attributes`**
 Filter an HTML element’s attributes during
the DOM processing pipeline. Parameters: `(array) $attributes`, `(string) $tag_name`,`(
array) $valid_attributes`

**`amp_wp_style_files_{$file}`**
 Filter CSS file content before it is added to 
the inline `<style amp-custom>` block. `{$file}` is the stylesheet handle. Parameter:`(
string) $css`

**`amp_wp_social_share_cache_time`**
 Set the cache duration in seconds for social
share counts. Default 7200 (120 minutes). Parameters: `(int) $seconds`, `(int) $
post_id`

**`amp_wp_social_share_count`**
 Filter social share count results after they are
fetched. Parameter: `(array) $results`

**`amp_wp_gdpr_country_list`**
 Customize the list of countries considered GDPR-
compliant. Parameter: `(array) $countries`

**`amp_wp_translation_std`**
 Provide or override fallback translation strings. 
Parameter: `(array) $translations`

#### Admin and Settings Hooks

**`amp_wp_settings_tab_menus`** (filter)
 Add a custom tab to the AMP WP settings
panel. Key is the tab slug, value is the tab label HTML. Parameter: `(array) $tabs`

    ```
    add_filter( 'amp_wp_settings_tab_menus', function( $tabs ) { $tabs['my-tab'] = '<span>My Tab</span>'; return $tabs; } );
    ```

**`amp_wp_settings_tab_section`** (action)
 Render the HTML content for your custom
settings tab. Check `$_GET['tab']` to target a specific tab.

**`amp_wp_save_setting_sections`** (action)
 Fires when the settings form is submitted.
Hook here to read and save data for a custom tab.

**`amp_wp_save_setting_notice`** (filter)
 Customize the “Settings saved.” notice
text. Parameter: `(string) $notice_text`

**`amp_wp_welcome_tab_menus`** (filter)
 Add a custom tab to the AMP WP welcome/
dashboard page. Parameter: `(array) $tabs`

**`amp_wp_welcome_tab_section`** (action)
 Render HTML content for a custom welcome
page tab.

**`amp_wp_default_configurations`** (action)
 Fires on plugin activation. Hook here
to set default options for add-on plugins.

## Screenshots

 * [[
 * Home Page
 * [[
 * Single Post Page
 * [[
 * Tags, Social Icons, Related Posts, Comments, and Footer
 * [[
 * Search & Archive Page
 * [[
 * 100% Valid AMP Content
 * [[
 * AMP WP Options Panel
 * [[
 * AMP Auto Ads Support
 * [[
 * Compatible with Major Cache Plugins
 * [[
 * Embed Images, Videos, Audios & iFrames
 * [[
 * Toggle Search & Header
 * [[
 * Recent Comments
 * [[
 * Sidebar, Social Icons & Related Posts
 * [[
 * Core WordPress Customizer Integration
 * [[
 * AMP WP Settings Panel

## FAQ

### What is the URL structure for AMP pages?

AMP WP supports two URL formats:

 1. **Prefix format:** `https://yoursite.com/amp/page-name`
 2. **Suffix format:** `https://yoursite.com/page-name/amp`

You can choose your preferred format from **AMP WP -> Settings -> General**.

### How do I add analytics tracking?

Go to **AMP WP -> Settings -> Analytics** in your WordPress admin dashboard. AMP
WP supports Google Analytics, Facebook Pixel, Segment, Quantcast, Alexa Metrics,
Chartbeat, comScore, Yandex Metrica, AFS Analytics, and Adobe Analytics.

### The post listing layout looks broken: how do I fix it?

This is usually caused by inconsistent thumbnail sizes. Install and run [Regenerate Thumbnails](https://wordpress.org/plugins/regenerate-thumbnails/)
to normalize all image sizes.

### Does AMP WP support Instagram Reels and TV posts?

Yes. Since version 1.6.0, AMP WP supports Instagram `/p/`, `/reel/`, and `/tv/` 
post types natively via `amp-instagram`.

### What prebuilt listing layouts are available?

Two layouts are included: **Classic View** and **List View**. Switch between them
from **AMP WP -> Options Panel**.

### How do I inject custom HTML (e.g., ad codes, analytics snippets)?

Go to **Dashboard -> AMP WP -> Customize AMP**. You can inject valid AMP snippets:
*
Between `<head></head>` tags * Right after the `<body>` opening tag * Right before
the `</body>` closing tag

Only valid, AMP-compatible code will function on AMP pages.

### How do I enable AdSense Auto Ads for AMP?

**Recommended path (AMP WP 1.7.7 and later):**

 1. Open **Dashboard -> AMP WP -> Settings -> General**.
 2. Scroll to the **Google Auto Ads** section.
 3. Tick the **Enable Google Auto Ads** checkbox and click **Save Changes**.

When the toggle is on, AMP WP automatically takes care of both halves that AdSense
Auto Ads needs on every AMP page:

 * The `amp-auto-ads` runtime loader (`<script async custom-element="amp-auto-ads"
   src="https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js"></script>`) is injected
   inside `<head>` via the canonical AMP component pipeline.
 * The matching `<amp-auto-ads type="adsense" data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
   >` element is emitted right after `<body>` so the AMP validator sees the extension
   as “used” and AdSense can place anchor, vignette, and in-page ads.

**Where does the publisher ID come from?**

 * **With Google Site Kit installed and AdSense connected:** the publisher ID is
   read automatically from Site Kit’s stored AdSense module setting (no extra typing
   needed).
 * **Without Site Kit:** supply your `ca-pub-...` value via the `amp_wp_adsense_publisher_id`
   filter. Drop this into your theme’s `functions.php` or a small mu-plugin:
 * add_filter( ‘amp_wp_adsense_publisher_id’, function() { return ‘ca-pub-XXXXXXXXXXXXXXXX’;});

The filter accepts any of the three plausible input shapes – `ca-pub-XXXX`, `pub-
XXXX`, or the bare numeric ID – and AMP WP normalises the value before output. If
no publisher ID is resolvable, AMP WP silently skips the body-side element so the
page stays validator-clean.

**Manual fallback (non-AdSense ad networks, custom snippets):**

Navigate to **Dashboard -> AMP WP -> Customize AMP**. Paste the runtime loader script
in the _“Codes between `<head>` and `</head>` tags”_ field, and your `<amp-auto-
ads>` (or other AMP-valid ad markup) in the _“Codes right after `<body>` tag”_ field.
When you later enable the native **Google Auto Ads** toggle, AMP WP auto-cleans 
any manually-pasted `amp-auto-ads` loader from the head-code field and surfaces 
a one-shot admin notice confirming the cleanup, so the native injection path remains
the single source of truth.

[AdSense Auto Ads for AMP – Google Help](https://support.google.com/adsense/answer/9011465)

### How do I enable or disable AMP for a specific post?

Edit the post in WordPress, scroll to the **AMP WP** meta box in the sidebar, and
toggle the AMP option on or off for that individual post.

### How do I report a bug or request a feature?

Please email `mohsin.rafique@gmail.com` or open a thread on the [WordPress support forum](https://wordpress.org/support/plugin/amp-wp/).

## Reviews

![](https://secure.gravatar.com/avatar/61f23dcc83a55eee501ecc2ebd9931032170542bd1889f8b5fd52fa108148c42?
s=60&d=retro&r=g)

### 󠀁[Лучший плагин](https://wordpress.org/support/topic/%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%b9-%d0%bf%d0%bb%d0%b0%d0%b3%d0%b8%d0%bd-26/)󠁿

 [newsroyals](https://profiles.wordpress.org/newsroyals/) July 31, 2022

Этой самый простой и лучший плагин AMP, который я пробовала. Если бы еще добавить
поддержку рекламных блоков – цены бы ему не было.

![](https://secure.gravatar.com/avatar/1cb548dfa82cb501a9ca412c91e5ec88fd4028ff6b52419d34fb8924a2e85749?
s=60&d=retro&r=g)

### 󠀁[Fast and easy plugin](https://wordpress.org/support/topic/fast-and-easy-plugin-3/)󠁿

 [lash](https://profiles.wordpress.org/ranc007/) June 21, 2022

Fast and easy plugin

![](https://secure.gravatar.com/avatar/f4b639c2c826265120c82e4a9bfe387b41f78095046f0d91da0664a90e42e54d?
s=60&d=retro&r=g)

### 󠀁[Works fine!](https://wordpress.org/support/topic/works-fine-1416/)󠁿

 [bf339](https://profiles.wordpress.org/bf339/) April 5, 2022

Fast settings for getting working page!

![](https://secure.gravatar.com/avatar/784be2e4e6252d029377690d26af0350bb4b4fdcdef281df6a26aeeacc233eb7?
s=60&d=retro&r=g)

### 󠀁[70](https://wordpress.org/support/topic/70-20/)󠁿

 [Adamu](https://profiles.wordpress.org/adamushuaibu/) March 17, 2022

Our photos

![](https://secure.gravatar.com/avatar/aade1543ffdc31e92868567231d599b9e8eecc60a13266af2a8148c8eb49e934?
s=60&d=retro&r=g)

### 󠀁[Works out of the box as advertised](https://wordpress.org/support/topic/works-out-of-the-box-as-advertised/)󠁿

 [docrpp](https://profiles.wordpress.org/docrpp/) March 10, 2022

Works out of the box. Just enough features for my use case. Two suggestions: 1. 
Would be nice to have a performance option: to enable server-side cache for amp 
pages specifically at the plugin level 2. Need an option to add “view desktop version”
to the header as well.

![](https://secure.gravatar.com/avatar/d56575991f929c2d92b5b1f17b5cc37ca934f6050ed75757426ca45be361e46d?
s=60&d=retro&r=g)

### 󠀁[Total abandon of support](https://wordpress.org/support/topic/total-abandon-of-support/)󠁿

 [jessik](https://profiles.wordpress.org/jessik/) February 16, 2022

The extension works better than the other AMP ones and is way more customizable.
However, the authors don’t bother answering to questions on the forum. Most of the
topics sat unsolved for more than 6 months. Just for that, it deserves a 2 star 
rating. You can’t just put a plugin out there and be like: “ok do your thing now,
not concerned anymore, bye”

 [ Read all 56 reviews ](https://wordpress.org/support/plugin/amp-wp/reviews/)

## Contributors & Developers

“AMP WP – Google AMP For WordPress” is open source software. The following people
have contributed to this plugin.

Contributors

 *   [ Pixelative ](https://profiles.wordpress.org/pixelative/)
 *   [ Mohsin Rafique ](https://profiles.wordpress.org/mohsinrafique/)

[Translate “AMP WP – Google AMP For WordPress” into your language.](https://translate.wordpress.org/projects/wp-plugins/amp-wp)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/amp-wp/), check out
the [SVN repository](https://plugins.svn.wordpress.org/amp-wp/), or subscribe to
the [development log](https://plugins.trac.wordpress.org/log/amp-wp/) by [RSS](https://plugins.trac.wordpress.org/log/amp-wp/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.7.11 – 2026-05-25

 * AMP-Validity: three-layer defense for invalid CSS at-rule stripping, resolving
   the `@view-transition` AMP validation error on sites running WordPress 7.0 with
   View Transitions enabled in global styles. (1) The content sanitizer now extracts`
   <style>` tags from `<head>` (not just `<body>`), feeding their CSS through `amp_wp_add_inline_style()`—
   this catches WordPress global-styles that previously bypassed the body-only extraction
   and appeared unsanitized in the AMP output. (2) Centralized the at-rule stripping
   regex into `Amp_WP_Styles::strip_invalid_atrules()` static method, called on 
   both input (`amp_wp_add_inline_style()`) and output (`Amp_WP_Styles::print_inline_styles()`).(
   3) The static method returns empty string on PCRE backtrack-limit failure rather
   than passing through unsanitized CSS.

#### 1.7.10 – 2026-05-25

 * AMP-Validity: strip CSS at-rules not allowed in AMP `<style amp-custom>` (e.g.`
   @view-transition`, `@import`, `@charset`, `@namespace`, `@layer`), including 
   when nested inside `@media` or `@supports` blocks. WordPress 7.0 adds `@media(
   prefers-reduced-motion:no-preference){@view-transition{navigation:auto}...}` 
   to its global-styles-inline-css output; the initial top-level-only regex missed`
   @view-transition` inside the `@media` wrapper. The sanitizer now runs a second
   pass with `preg_replace_callback` that opens allowed container at-rules and strips
   invalid nested at-rules from their content, collapsing the container entirely
   if it becomes empty.
 * Fix: six IDE diagnostics in `includes/functions/amp-wp-theme-functions.php`. (
   1) Split `amp_wp_direction()` (void/echo) into `amp_wp_get_direction()` (returns
   string) + a thin echo wrapper so the concatenation at line 1907 no longer uses
   a void return value; the existing template caller in `sidebar.php` continues 
   to work unchanged. (2) Replaced the broken `Amp_WP_Public::get_instance()->call_components_method()`
   call in `amp_wp_do_shortcode()` — `Amp_WP_Public` has no `get_instance()` method—
   with a direct iteration over the `$amp_wp_registered_components` global, matching
   the same logic the instance method uses. (3) Fixed possibly-undefined `$area_label`
   and `$param` in `amp_wp_social_share_get_li()`: the `facebook` switch case only
   set these variables when `facebook_app_id` was present; when missing, execution
   fell through to code that used them. Inverted the guard to return early when 
   the app ID is absent. (4) Fixed `amp_wp_comment_reply_link()` assigning the void
   return of `comment_reply_link()`: set `$args['echo'] = false` so WordPress returns
   the markup as a string instead of echoing it directly, then echo + return the
   result. Remaining P1125 is an Intelephense limitation — it cannot narrow `void
   |string` based on the runtime `echo` key.
 * Refactor: WPCS cleanup of `includes/functions/amp-wp-core-functions.php`. Fixed
   all 36 errors and 4 warnings: added missing `@param` descriptions across 6 functions(`
   amp_wp_enqueue_style`, `amp_wp_enqueue_script`, `amp_wp_register_component`, `
   amp_wp_guess_non_amp_url`, `amp_wp_translation_get`, `amp_wp_translation_echo`),
   corrected swapped `@param` names on `amp_wp_add_inline_style` and `amp_wp_enqueue_inline_style`,
   fixed `@return bool` on void function, added `@throws` tag, added translators
   comment, removed structured-array hash notation that tripped `ParamCommentFullStop`,
   refactored two assignment-in-return statements, added param types for three untyped
   parameters (`$key`, `$key`, `$parsed_url`), renamed reserved-keyword parameters(`
   $var` -> `$found`, `$echo` -> `$should_echo`), fixed inline `@var` annotation
   format, and terminated all param comments with full stops. Removed dead `mysql_get_server_info()`
   branch in `amp_wp_get_server_database_version()` (the `mysql_*` extension was
   removed in PHP 7.0; WordPress itself dropped this codepath in 5.x). Fixed `@param
   null` -> `@param WP_Query|null` on `is_amp_wp()`. No behaviour change.
 * Refactor: WPCS cleanup of `includes/functions/amp-wp-formatting-functions.php`.
   Fixed 1 error + 2 warnings: added missing file docblock, renamed reserved-keyword
   parameter `$var` to `$tooltip` in `amp_wp_sanitize_tooltip()` and to `$value`
   in `amp_wp_clean()`. No behaviour change.
 * Refactor: WPCS cleanup of `includes/functions/amp-wp-utility-functions.php`. 
   Fixed 4 errors + 3 warnings: renamed reserved-keyword parameter `$string` to `
   $url` in `amp_wp_remove_query_string()` and added missing description, moved `
   Copyright:` URL lines to proper `@link` tags so PHPCS no longer treats them as
   unterminated param comments, added missing `return` statement to `amp_wp_remove_class_action()`(
   was silently discarding the boolean from `amp_wp_remove_class_filter()`), switched
   loose `==` to strict `===` in `amp_wp_transpile_text_to_pattern()`, replaced `
   urlencode()` with `rawurlencode()` in `amp_wp_parse_url()`. No behaviour change.
 * Refactor: WPCS cleanup of `includes/functions/amp-wp-ad-functions.php`. Fixed
   3 errors: added missing `@param` descriptions on `amp_wp_get_ad_location_data()`
   and `amp_wp_show_ad_location()`, corrected `@return array` to `@return void` 
   on `amp_wp_show_ad_location()`. No behaviour change.

#### 1.7.9 – 2026-05-21

 * Compat: verified and declared WordPress 7.0 compatibility. Tested up to WordPress
   7.0.
 * Refactor: PHPCS cleanup of `includes/components/class-amp-wp-img-component.php`.
   Fixed all PHPDoc issues (missing param descriptions, missing return tags, missing
   summary periods, incorrect tag ordering), removed assignment-in-condition, fixed
   closing braces on wrong lines, removed commented-out code, fixed inline comment
   formatting. Moved the standalone `amp_wp_create_image()` function to `includes/
   functions/amp-wp-core-functions.php` to resolve `Universal.Files.SeparateFunctionsFromOO.
   Mixed` (a file must not contain both class and function declarations). No public-
   API change; function remains available at the same name.
 * Refactor: PHPCS cleanup of `includes/components/class-amp-wp-instagram-component.
   php`. Added separate file and class docblocks (resolves `Squiz.Commenting.FileComment.
   Missing` and `Squiz.Commenting.ClassComment.SpacingAfter`), fixed PHPDoc tag 
   ordering and added missing param/return descriptions, removed inline `@var` type
   hint flagged as commented-out code, added missing summary periods. Standardized
   file docblock tags to `@package`/`@subpackage`/`@author`/`@copyright`/`@since`
   order.
 * Refactor: PHPCS cleanup of `includes/components/class-amp-wp-playbuzz-component.
   php`. Added separate file and class docblocks, fixed assignment-in-condition,
   removed inline `@var` type hint, added missing PHPDoc summaries and param/return
   descriptions, standardized file docblock tag ordering, added `@subpackage` tag.
   No behaviour change.
 * Refactor: cleared all 102 WPCS errors and 40 warnings from `includes/functions/
   amp-wp-theme-functions.php` (the 2677-line template / globals / properties / 
   queries / social-share / comments helper library). No public-API surface change;
   file now passes `phpcs --standard=WordPress` with exit 0. PHP syntax check clean.
   Highlights: replaced two deprecated constants (`STYLESHEETPATH` -> `get_stylesheet_directory()`,`
   TEMPLATEPATH` -> `get_template_directory()`) inside the template-resolution scan
   loop in `amp_wp_locate_template()`; backfilled `@param` descriptions on more 
   than thirty docblocks across the file (every helper from `amp_wp_load_template()`
   and `amp_wp_hw_attr()` through the `amp_wp_set_global` / `amp_wp_get_prop` cluster,`
   amp_wp_get_branding_info()`, `amp_wp_get_theme_mod()`, `amp_wp_get_option()`,
   the query helpers, the comment helpers, `amp_wp_min_suffix()`, `amp_wp_collect_post_type_slugs()`,
   and the social-share trio); flipped six Yoda violations and four loose-comparison`!
   =` / `==` to strict `!==` / `===` (including the localhost check, the customizer-
   direction toggle, the menu-walker theme-location compare, the social-share format
   compare and the taxonomy slug compare); refactored eight assignment-in-condition
   patterns into explicit fetch-then-test ladders (front-page resolver, branding
   info, rel-canonical print, locate_template return scan, site-url permalink-prefix
   block); flagged six `phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames`
   on the public signatures for `amp_wp_locate_template($require_once)`, `amp_wp_load_template(
   $require_once)`, `amp_wp_body_class($class)`, and four `$default` parameters 
   across the global / prop accessors – renaming would have broken sites that call
   these by name; tagged the historical `amp-wp-template-default-theme-mod` filter
   at `amp_wp_get_option()` with a `phpcs:ignore WordPress.NamingConventions.ValidHookName.
   UseUnderscores` plus a justification explaining why renaming to underscores would
   break any site filtering this 1.0.0-era public hook; added a single `phpcs:ignore`
   on the `wp_reset_query()` call inside `amp_wp_clear_query()` with a paragraph-
   long justification about why `wp_reset_postdata()` would not undo a custom `amp_wp_set_query()`
   swap; replaced `rand()` with `wp_rand()` in `amp_wp_element_unique_id()`; removed
   the `@` error-silencing on `number_format()` inside `amp_wp_human_number_format()`(
   cast to float instead, which is the actual fix – non-numeric inputs are already
   rejected by the early return above); added the missing `/* translators: */` comment
   on the taxonomy archive title `sprintf( __( '%1$s: %2$s' ) )` in `amp_wp_get_archive_title_fields()`
   and corrected its textdomain from the placeholder `beetter-amp` to `amp-wp`; 
   collapsed twelve inline comments to proper full-stop terminators; corrected the
   bogus `@return int` on `amp_wp_comment_link()` (the function only echoes) to `
   @return void`; tightened the `$_GET` whitelist intersect in `amp_wp_get_canonical_url()`
   so the existing nonce-recommended ignore lands on the exact `$_GET` token rather
   than the line above; and scoped a `phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter`
   on `amp_wp_comment_item()` whose `$comment` / `$args` / `$depth` parameters are
   required by the `Walker_Comment` callback signature and are read inside the included`
   comment-item.php` template via scope inheritance. Removed dead code: the long
   commented-out Google Plus social-share block inside `amp_wp_social_share_fetch_count()`(
   the service was retired by Google in 2019; the `case 'google_plus':` body had
   been mid-deletion for years, leaving unreachable `if ( ! is_wp_error( $remote))`
   debris after the preceding `break;`), plus the dangling `'google_plus'` entry
   in the `amp_wp_social_shares_count()` valid-sites whitelist that was the only
   remaining live reference to it.
 * Refactor: cleared all 8 WPCS errors and 2 warnings from `includes/components/
   class-amp-wp-carousel-component.php` (the carousel component that renders WordPress
   galleries and `[amp-wp-slider]` post sliders as `<amp-carousel>`). No behaviour
   change; file now passes `phpcs --standard=WordPress` with exit 0. Added the missing
   file-level docblock; gave `transform()`, `handle_gallery()`, `handle_slider()`,`
   slider_posts()`, and `gallery_attachments()` proper `@param` descriptions; added
   short descriptions to the two `protected` renderers that had `@param`-only blocks.
   The `slider_posts()` docblock now carries a multi-paragraph “Uses query_posts()
   intentionally because…” explanation mirroring the existing `Amp_WP_Public::amp_wp_set_page_query()`
   docblock that documents the equivalent front-page override: the slider template
   iterates via `amp_wp_have_posts()` / `amp_wp_the_post()` (in `includes/functions/
   amp-wp-theme-functions.php`) which fall back to the global `$wp_query` / `$wp_the_query`
   when no custom query has been staged via `amp_wp_set_query()`, so a fresh `WP_Query`
   would silently iterate the page’s original main query and a `wp_reset_postdata()`
   paired with it would only rewind the post pointer and leave the slider’s query
   active for the rest of the request. `query_posts()` and `wp_reset_query()` are
   both `phpcs:ignore`d with short `-- see method docblock` references. Also removed
   a dead local variable in `config()` (`$array_mohsin`) that built an array identical
   to the one already being returned literally below it; safe deletion (zero references,
   return value unchanged).
 * Refactor: cleared all 40 WPCS errors and 4 warnings from `includes/components/
   class-amp-wp-iframe-component.php` (the iframe component that bridges YouTube/
   Twitter / Facebook / Vimeo / SoundCloud / Instagram onto AMP component tags).
   No behaviour change; the file now passes `phpcs --standard=WordPress` with exit
   0. Highlights: added the missing file-level docblock; backfilled missing `@param`
   descriptions on 16 methods; renamed the `$string` parameter on `get_iframe_dimension()`
   to `$html` to drop the reserved-keyword conflict (all four call sites within 
   the same file are positional, no external caller risk); added the missing short
   description on `$enable_enqueue_scripts`; switched `in_array( $provider, $this-
   >support_sites )` to strict mode; refactored the two assignment-in-condition 
   patterns inside `transform()` into explicit fetch-then-test ladders; gave `get_frame_height()`
   a typed `@param string $url`; ended the inline `// Fix: remove aparat iframe 
   extra tags` comment with a full stop; and scoped a single `phpcs:disable WordPress.
   NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase` block around the
   four `$element->parentNode` / `$element->previousSibling` DOM-API reads in `sanitize()`.
 * Fix: `Amp_WP_Html_Util::set_outer_HTML()` now actually replaces the target element
   with the parsed HTML in-place. The previous implementation appended the parsed
   fragment to the end of the parent’s children, then ran a `while` loop that wiped
   every child of the parent (including the fragment it had just added and every
   sibling of the target element). In practice the only caller, the iframe component’s
   SoundCloud rewrite branch (`includes/components/class-amp-wp-iframe-component.
   php:325`), only landed correct output when the iframe happened to be the sole
   child of its container; in any other layout, surrounding markup was silently 
   destroyed. New implementation guards on `$element->parentNode`, builds a `DOMDocumentFragment`,
   parses the HTML under suppressed libxml errors (clearing the queue afterwards
   so noisy embed markup does not pollute downstream handlers), and uses `replaceChild(
   $fragment, $element )` so the fragment dissolves into its child nodes at the 
   original element’s slot in the parent’s child list. Sibling elements are preserved.

#### 1.7.8 – 2026-05-14

 * AMP-Validity: emit the `<amp-auto-ads type="adsense" data-ad-client="ca-pub-XXXX"
   >` body-side element whenever the General Settings “Google Auto Ads” toggle is
   on and an AdSense publisher ID is resolvable. Closes the validator error “The
   extension ‘amp-auto-ads’ was found on this page, but is unused. Please remove
   this extension.” that surfaced when the toggle was enabled alongside Google Site
   Kit: Site Kit’s AdSense AMP integration only fires for the official Automattic
   AMP plugin (it detects `amp_is_request()` / `AMP__VERSION` / the `amp_post_template_footer`
   hook), so on AMP WP routes Site Kit never injected the body element that the 
   runtime loader expects to consume. AMP WP now emits the element itself. Publisher
   ID resolution chain: (1) `amp_wp_adsense_publisher_id` filter for theme/plugin
   overrides, (2) Site Kit’s stored AdSense module setting `googlesitekit_adsense_settings['
   clientID']` – the same key already inspected by `amp_wp_is_site_kit_adsense_active()`.
   Returned value is normalised to `ca-pub-XXXXXXXXXXXXXXXX`, accepting any of the
   three plausible input shapes (`ca-pub-XXXX`, `pub-XXXX`, or the bare numeric 
   ID). When no publisher ID is configured anywhere the element is silently skipped
   so the page stays validator-clean, and when the toggle is off neither the loader
   nor the element is emitted. New public API: function `amp_wp_get_adsense_publisher_id()`,
   function `amp_wp_render_auto_ads_element()`, filter `amp_wp_adsense_publisher_id`.
   Runtime path: `amp_wp_render_auto_ads_element()` hooks `amp_wp_template_body_start`
   at priority 5 (runs before `amp_wp_custom_code_body_start` so a manual snippet
   in the body-start Customizer field continues to layer on top), gates on `amp_wp_is_auto_ads_enabled()`,
   and uses `printf` with `esc_attr()` on the `data-ad-client` value. No new option
   storage and no UI field added in this round (publisher ID is auto-resolved from
   Site Kit, or a filter override): cleanest possible diff that fixes the validator
   error without scope-creep into an Ads-Manager-style settings UI, which belongs
   in the `amp-wp-ads-manager` add-on.

#### 1.7.7 – 2026-05-14

 * Feature: native “Google Auto Ads” toggle on the AMP WP > Settings > General page.
   When enabled, AMP WP enqueues the canonical `amp-auto-ads` runtime loader (`https://
   cdn.ampproject.org/v0/amp-auto-ads-0.1.js`) on every AMP page via the existing`
   amp_wp_enqueue_script()` registry, replacing the long-standing workaround of 
   pasting the script into the Customizer’s “Codes between and tags” field. Default
   off; opt-in. Runtime path: `amp_wp_enqueue_auto_ads_script()` is hooked on the
   existing `amp_wp_template_enqueue_scripts` action and calls `amp_wp_enqueue_script('
   amp-auto-ads', 'https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js' )` – identical
   pattern to how every other AMP component loader is registered (`amp-form`, `amp-
   consent`, `amp-geo`, etc.), so the resulting `<script async custom-element="amp-
   auto-ads" src="..."></script>` tag is rendered by the same `amp_wp_print_scripts()`
   callback that emits every other component and benefits from automatic dedupe 
   against any sibling caller that registers the same handle. Backward compatibility:
   when the head-code Customizer field already contains an `amp-auto-ads` loader,
   the enqueue is skipped (duplicate guard via `amp_wp_head_field_has_auto_ads_loader()`);
   on save, AMP WP also strips that manually-pasted loader from the head-code field
   via `amp_wp_auto_ads_strip_manual_loader()` so the native path becomes the single
   source of truth, surfacing a one-shot dismissible admin notice confirming the
   cleanup. The settings UI shows two contextual notices: an amber heads-up when
   the head-code field still contains a loader (preview of what enabling the toggle
   will clean up) and a blue recommendation when Google Site Kit is detected active
   with AdSense connected (`amp_wp_is_site_kit_adsense_active()` checks `is_plugin_active('
   google-site-kit/google-site-kit.php' )` plus the AdSense module’s stored `clientID`
   in `googlesitekit_adsense_settings`). New helpers: `amp_wp_is_auto_ads_enabled()`,`
   amp_wp_is_site_kit_adsense_active()`, `amp_wp_head_field_has_auto_ads_loader()`,`
   amp_wp_enqueue_auto_ads_script()`, `amp_wp_auto_ads_strip_manual_loader()`. Migration
   logic hooks both `update_option_amp_wp_general_settings` (subsequent saves) and`
   add_option_amp_wp_general_settings` (first-ever save where the option does not
   yet exist – WordPress fires `add_option_<name>` instead of `update_option_<name
   >` in that branch). New option key `amp_wp_general_settings['google_auto_ads']`(
   boolean 0/1); save handler always writes a 0/1 explicitly even when the checkbox
   is unchecked, so the off-transition is recorded in `$new_value` for the migration
   diff. No public-API breakage; existing manually-pasted loaders in the head-code
   Customizer field continue to work unchanged when the toggle is left off.
 * Fix: amp-geo “country … not valid, will be ignored” runtime error on every page
   with the GDPR consent banner enabled. The `ISOCountryGroups.eu` array inside 
   the `<amp-geo>` JSON config was being emitted through `esc_js()`, which is the
   wrong tool for JSON content: it HTML-encodes `"` as `&quot;`, so amp-geo received
   a single corrupted string like `PK","AT","BE",...,"CH"` instead of an array of
   ISO country codes, and silently rejected the whole config (breaking geo-gated
   consent behaviour). Replaced the `esc_js( implode( '","', ... ) )` pattern at`
   public/partials/tez/components/gdpr/gdpr.php:22` with `wp_json_encode( array_values(
   array_map( 'sanitize_text_field', $gdpr_countries ) ) )` so the country list 
   is emitted as a valid JSON array. The `amp_wp_gdpr_country_list` filter contract
   is unchanged.
 * Refactor: WordPress Coding Standards (WPCS) cleanup of the Tez theme directory(`
   public/partials/tez/`). All 37 template files now pass `phpcs --standard=WordPress`
   with zero errors and zero warnings, down from 49 errors and 31 warnings across
   28 files. Highlights: every inline `//` comment now ends with proper punctuation;
   all loose-comparison toggle checks like `'1' == $foo_switch` are replaced with`!
   empty( $foo_switch )` (handles both string `'1'` and integer `1` saved values
   uniformly without behaviour change); the file-level docblock is present and correctly
   spaced on every template (added to `header.php`, `footer.php`, `shortcodes/amp-
   slider.php`, `shortcodes/gallery.php`, and `shortcodes/index.php`; corrected 
   spacing on seven existing docblocks that were flush against the next code line);
   template-local variables that shadowed WP globals were renamed (`$id` -> `$slider_id`/`
   $thumbnail_wrapper_id` / `$related_item_id`, `$link` -> `$comment_link`, `$title`-
   > `$archive_title`); Yoda conditions applied in `components/social-list/social-
   share.php`; assignments lifted out of `if` conditions in `attachment.php` and`
   social-share.php`; dead commented-out Google Plus social-share scaffolding (a
   service Google retired in 2019) removed from `components/social-list/social-links.
   php`; the slider’s `meta_query` for featured-image gating is annotated with `//
   phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query` + justification
   rather than restructured (no equivalent rewrite exists without changing semantics).
   All changes are sniff-driven cleanups with no rendering behaviour change.
 * Refactor: removed `public/partials/tez/amp-wp-ads-manager/ads.php` and its parent
   directory from core. The file (a half-completed placement-manager UI sketch from
   an earlier design – never `require`-d anywhere, hooks two filters that are never
   fired, calls two helper functions that are not defined) was relocated verbatim
   to the `amp-wp-ads-manager` add-on at `includes/placement-manager/ads.php` to
   preserve its design intent for future completion in the right plugin. Reinforces
   the family architecture rule that core no longer references any add-on slug in
   any directory path; no public-API change, no add-on consumed the file from this
   location.

#### 1.7.6 – 2026-05-12

**Security**

 * Comprehensive WordPress Coding Standards security sweep across the entire plugin.
   Output escaping (`esc_html`, `esc_attr`, `esc_url`, `wp_kses`, `wp_kses_post`)
   applied to every flagged `echo` / `printf` site in admin partials, settings forms,
   public templates, customizer controls, and core functions. Superglobal reads (`
   $_SERVER['REQUEST_URI']`, `$_SERVER['SCRIPT_FILENAME']`, `$_SERVER['PHP_SELF']`,`
   $_SERVER['HTTP_USER_AGENT']`, `$_SERVER['SERVER_SOFTWARE']`, `$_SERVER['LOCAL_ADDR']`,`
   $_SERVER['SERVER_ADDR']`) now consistently guarded with `isset()` and run through`
   esc_url_raw( wp_unslash() )` or `sanitize_text_field( wp_unslash() )` as appropriate
   per use site. Translation strings with intentional HTML (e.g. `<strong>` for 
   inline emphasis) now route through `wp_kses_post( __() )` instead of bare `_e()`
   so the markup is preserved while bot-injected HTML is filtered out. Legacy `//
   WPCS: XSS ok.` comments replaced with modern `// phpcs:ignore WordPress.Security.
   EscapeOutput.OutputNotEscaped --` annotations with a one-line justification each.
   Net effect: the `WordPress.Security.EscapeOutput`, `WordPress.Security.NonceVerification`,
   and `WordPress.Security.ValidatedSanitizedInput` sniffs report 0 errors and 0
   warnings across the plugin (down from 311 errors before the sweep). Total WPCS
   reduction: 1,448 -> 632 (56%). No behavioural change – all edits are escaping/
   sanitizing additions or `phpcs:ignore` annotations on canonical safe-output patterns.

**Added**

 * New “Premium Extensions” tab on the AMP WP Settings page (`admin.php?page=amp-
   wp-settings#settings-premium-extensions`). Central admin home for AMP WP family
   add-ons that need configurable settings without forking a full settings tab. 
   Add-ons populate the tab via the new `amp_wp_premium_extensions_section` action–
   callbacks echo a card (heading + `form-table` row group) inside the tab body,
   and the existing `amp_wp_save_setting_sections` action persists each add-on’s
   option independently. New files: `includes/admin/settings/class-amp-wp-premium-
   extensions.php` (registers the tab) and `admin/partials/settings/amp-wp-admin-
   premium-extensions.php` (the form scaffold).
 * Premium-extension registration API: `amp_wp_register_premium_extension( string
   $slug, array $args = [] ): void`, `amp_wp_get_registered_premium_extensions():
   array<string,array<string,string>>`, and `amp_wp_has_active_premium_extension():
   bool` (all in `includes/functions/amp-wp-core-functions.php`). Each AMP WP family
   add-on self-registers once on bootstrap with a slug + display metadata (`name`,`
   description`, `version`, `url`). Idempotent: registering the same slug twice 
   is a no-op. New companion filter `amp_wp_premium_extensions` for advanced reordering,
   suppression, or unit-test snapshot/restore. The empty-state partial reads the
   registry to render the active-extensions list (instead of hard-coding “AMP WP–
   Contact Form 7”).
 * Template-path registration: new public functions `amp_wp_register_template_path(
   string $absolute_path ): void` and `amp_wp_get_registered_template_paths(): array`(
   in `includes/functions/amp-wp-theme-functions.php`) let AMP WP family add-ons
   ship their own AMP templates instead of having them live inside core’s “Tez” 
   theme directory. Resolution order is `<wp-theme>/amp-wp/<template>` (site override–
   unchanged) -> registered add-on paths in registration order -> core’s Tez directory
   as the catch-all fallback. End-user override convention preserved verbatim. New
   filter `amp_wp_template_paths` lets advanced users reorder or suppress paths 
   per-request. Companion change in `amp-wp-woocommerce` migrates eight WC-specific
   AMP partials out of core to restore the family architecture rule “Core never 
   references any add-on by name”.
 * New extension hook `amp_wp_layout_setting_after_show_comments` fires inside the
   Layout Settings -> Single Post Page table, immediately after the “Show Comments”`
   <tr>`. Designed for AMP WP family add-ons (e.g. AMP WP – Comments uses it to 
   print a “Show Comment Replies” toggle row) to extend the comment-related settings
   block without forking `admin/partials/settings/amp-wp-admin-layout.php`. Callbacks
   must echo a complete `<tr>...</tr>` matching the surrounding form-table markup;
   inputs may piggyback on the existing `amp_wp_layout_settings[...]` name prefix
   to persist via the existing save handler at `Amp_WP_Layout::amp_wp_save_layout_settings()`–
   no separate option, no separate save flow.

**Changed**

 * The “Premium Extensions” tab is now hidden by default – it only registers when
   at least one AMP WP family add-on (CF7, Comments, WooCommerce, WPForms, Gravity
   Forms, Ads Manager) has called the new `amp_wp_register_premium_extension()` 
   function on its bootstrap. Previously the tab appeared on every install, even
   on a fresh one with no add-ons active, where it rendered an empty-state hint.
   Back-compat fallback: if an older add-on hooks `amp_wp_premium_extensions_section`
   directly without registering, the action presence still triggers the tab. Both
   the tab definition and the partial loader gate on the same check, so a stale 
   anchor link to `#settings-premium-extensions` no longer paints orphaned form 
   markup either.

**Fixed**

 * Browsers fetching `/favicon.ico` (and any other root-level static-file URL) on
   mobile no longer trigger a 404. `Amp_WP_Public::amp_wp_auto_redirect_to_amp()`
   fed `$_SERVER['REQUEST_URI']` through `Amp_WP_Content_Sanitizer::transform_to_amp_url()`,
   which blindly appended `/amp` to any internal URL it didn’t already recognise
   as an AMP path or wp-content asset. When a site has no static `favicon.ico` file
   at the document root, the browser-issued favicon request fell through to `index.
   php`, hit `template_redirect` on mobile, and the auto-redirect bounced the browser
   to `/favicon.ico/amp` – which never matches a rewrite rule and 404s. Same shape
   would fire for any root-level static file (`sitemap.xml`, `robots.txt`, `ads.
   txt`, `manifest.json`, etc.) when no physical file exists. Fix: `transform_to_amp_url()`
   now short-circuits and returns the URL unchanged when the path’s basename has
   a static-file extension (35-entry list covering icons, images, scripts, styles,
   fonts, archives, media, metadata). List is filterable via the new `amp_wp_non_amp_url_extensions`
   filter so unusual extensions can be added per-site. Case-insensitive match. Side
   benefit: every other call site of `transform_to_amp_url()` – filters on `term_link`,`
   author_link`, `attachment_link`, `post_type_link`, the nav-menu `href` filter,
   the canonical-URL helper – is now also protected from inadvertently rewriting
   static-file URLs that themes or third-party plugins happen to pass through them.
 * Search-engine crawlers no longer get caught by the mobile auto-redirect to AMP.`
   Amp_WP_Public::amp_wp_auto_redirect_to_amp()` previously checked only `wp_is_mobile()`,
   which returns true for Googlebot-Mobile, Bingbot, YandexBot, DuckDuckBot, AppleBot,
   FacebookExternalHit, Twitterbot, LinkedInBot, Slurp, AhrefsBot, and SemrushBot.
   Each was being 302-redirected from `https://yoursite.com/` to `https://yoursite.
   com/amp/`; the AMP page’s `<link rel="canonical">` then pointed back at the original,
   and Google Search Console flagged the canonical URL as “Redirect error” in Page
   Indexing because the URL it was trying to index kept redirecting. Google’s AMP
   guidelines explicitly require the canonical to be served directly to bots; AMP
   discovery happens via the head-tag `<link rel="amphtml">`, not a UA redirect.
   Fix: new public helper `amp_wp_is_search_engine_bot(): bool` in `includes/functions/
   amp-wp-core-functions.php` checks `HTTP_USER_AGENT` against a 12-entry list (
   substring match, case-insensitive); the redirect path bails early when it returns
   true. The cache-plugin desktop fallback (which injects the UA-sniffing JS redirect)
   is implicitly safe because the server-side branch already short-circuited for
   bots. Filterable via `amp_wp_search_engine_bot_user_agents` (UA fragment list)
   and `amp_wp_is_search_engine_bot` (final boolean). Unit-tested against 10 real-
   world UA strings (7 bots + 3 real users) – 10/10 pass. After the fix ships, click“
   Validate fix” inside Search Console; Google re-crawls the affected URLs in 1-
   7 days and the “Redirect error” count drops to 0.
 * Pagination on AMP archives was broken for any URL of the form `/<archive>/amp/
   page/N/`. The URL-based branch of `is_amp_wp()` (used during `pre_get_posts` 
   before `template_redirect` fires, when the `amp` query var is not yet set on `
   $wp_query`) only matched the AMP token at the very start or very end of the request
   URI – so `/shop/amp/page/2/`, `/product-category/<slug>/amp/page/2/`, `/category/
   <slug>/amp/page/2/`, `/tag/<slug>/amp/page/2/`, and `/<page>/amp/comment-page-
   N/` all returned `false`. The cascade was severe: any third-party `pre_get_posts`
   callback gated on `is_amp_wp()` (notably the AMP WP – WooCommerce add-on’s priority-
   0 bridge that re-registers `WC_Query::pre_get_posts` at priority 101) bailed 
   early, AMP WP’s query isolation between priorities 1 and 100 wiped WooCommerce’s
   product-archive setup, and `/shop/amp/page/2/` rendered the shop _page_ (post
   type `page`, ID 588) with `paged=2` instead of the products grid. The old second
   regex also had a separate false-positive bug where `amp/*` allowed zero trailing
   characters and matched URIs like `/ampersand/` and `/amp-tree/`. Both regexes
   replaced with a single boundary-anchored pattern `#(?:^|/)<amp>(?:/|$)#` that
   detects AMP as a whole path segment anywhere in the URI; verified against 20 
   URL patterns (20/20 pass) including paginated archives, sub-directory installs,
   and the previously-broken false positives. Live verification on `/shop/amp/page/
   2/`: `post_count=8 found_posts=50 max_num_pages=7 paged=2 post_type=product is_archive
   =1 is_post_type_archive=1 is_amp_wp=1 is_shop=1 is_woocommerce=1` (was `post_count
   =1 paged=2 post_type="" is_archive=0 is_page=1 is_amp_wp=0` before the fix).
 * The “Premium Extensions” tab was rendering an empty `<i>` tag with no visible
   icon next to the label. The original `amp-wp-admin-icon-star` class did not exist
   in `admin/css/amp-wp-admin.css` or in the icon font at `admin/fonts/amp-wp-admin-
   icons/`. Switched to `amp-wp-admin-icon-feather`, which is defined and not already
   in use by another tab (every star / puzzle / rocket / crown glyph variant is 
   missing or taken). CSS and font are unchanged.

**Docs**

 * Inline help text added to five settings tabs: Structured Data, GDPR Compliance,
   Notice Bar, Layout Settings, and General Settings. Around 30 new `<p class="description"
   >` blocks under every previously-undocumented option so admins do not need to
   consult external docs to know what each toggle / select / textarea does. Highlights:
   the GDPR master toggle now explains the EU/EEA IP gate and the third-party-script
   suppression behaviour; Layout’s “Show Slider on Home Page”, “Show Share Box In
   Posts”, and “Show Related Posts” parent toggles each get a description plus descriptions
   on every sub-control; General’s “Enable AMP on Home Page”, “Enable AMP on Search
   Page”, “Disable AMP on Post Types”, and “Disable AMP on Taxonomies” finally have
   inline guidance. All strings wrapped in `esc_html_e( ..., 'amp-wp' )` using the
   same `<p class="description">` pattern already established by the GDPR partial.
   No PHP / behaviour changes – pure documentation in the admin UI.
 * Inline help text added under the OneSignal options on the 3rd Party Plugins tab(`
   admin.php?page=amp-wp-settings#settings-third-party-plugins-support`). Three 
   new `<p class="description">` blocks: under “App ID” (UUID format and where to
   find it – OneSignal dashboard -> Settings -> Keys & IDs), under “Position” (subscription
   bell placement and when to override the default), and under “Subdomain” in the
   HTTP Site row (OneSignal-hosted HTTPS subdomain like `yoursite.os.tc` that non-
   HTTPS sites need for the push subscription frame; HTTPS sites should leave the
   field blank).

#### 1.7.5 – 2026-05-07

 * Fixed: Mobile browsers no longer get stuck in a “too many redirects” loop on 
   AMP URLs after an admin changes “Exclude AMP by URL” or “Exclude URLs From Auto
   Converting”. Plugin-issued redirects (AMP-router and mobile auto-redirect) now
   downgrade the AMP-excluded redirect from `301 Moved Permanently` to `302 Found`
   and emit `nocache_headers()`, so neither browsers, proxies, nor CDNs can pin 
   a stale redirect direction once the underlying setting flips. The desktop-side
   fallback that injects a JavaScript redirect to the AMP URL when a caching plugin
   is detected now also defines `DONOTCACHEPAGE` and emits no-cache headers, preventing
   cache plugins that do not separate cache by device from serving a desktop response(
   which contains the mobile-UA-sniffing redirect script) to mobile visitors and
   triggering an infinite loop in the mobile browser. Existing affected clients 
   are auto-recovered on their next request to a now-AMP-excluded URL: the AMP-router
   redirect response carries a one-shot `Clear-Site-Data: "cache"` header (HTTPS-
   only, gated by an `amp_wp_cache_cleared` cookie marker so normal HTTP caching
   is preserved on subsequent redirects), which evicts any stale `301` pinned in
   the mobile browser before it follows the redirect.
 * Fixed: `amp-state` elements are no longer stripped from AMP pages when the AMP
   WP Comments plugin is active. The content sanitizer’s allowed-tag list (`tags-
   list.php`) did not include `amp-state`, causing the element to be silently removed
   during the HTML clean-up pass, which broke the AMP threading state used by the
   comment reply form.
 * Fixed: Per-comment Reply links now appear in threaded comment lists on AMP pages.`
   amp_wp_comment_item()` was not forwarding the `$depth` argument from `wp_list_comments()`
   to the `comment-item.php` template. WordPress 6.8 updated `get_comment_reply_link()`
   to return null when depth is 0, so every reply link resolved to nothing. The 
   function now accepts and passes `$depth` (defaulting to 1).
 * Fixed: Per-comment Reply button now shows the label “Reply” instead of the comment
   form title “Leave a Reply”. The template was pulling from `amp_wp_translation_get('
   comments_reply')`, which returns the form heading. The button now uses `__('Reply','
   amp-wp')` directly.
 * Fixed: AFS Analytics configuration JSON was malformed. Missing commas after `
   websiteid` and `title` properties caused a JSON parse error in the AMP runtime,
   silently disabling analytics on AMP pages when AFS Analytics was configured.
 * Improved: `includes/class-amp-wp-redirect-router.php` brought into WordPress 
   Coding Standards compliance. Added a proper file-level docblock (the previous
   one only documented the class). Sanitized `$_SERVER['REQUEST_URI']` via `isset()`
   + `esc_url_raw( wp_unslash() )` in `redirect_to_amp_url()`. Renamed the singleton
   accessor `Run()` to `run()` to satisfy WPCS snake_case naming; the class is documented
   as not-public in the family’s public API contract, so this rename is internal
   and the single caller in `Amp_WP_Public::define_public_hooks()` was updated. 
   None of the six add-ons (cf7, comments, woocommerce, wpforms, gf, ads-manager)
   reference the class.
 * Improved: `public/class-amp-wp-public.php` brought into WordPress Coding Standards
   compliance. Sanitized and unslashed all `$_SERVER['REQUEST_URI']` and `$_SERVER['
   HTTP_HOST']` reads via `esc_url_raw( wp_unslash() )` / `sanitize_text_field( 
   wp_unslash() )` with `isset()` guards in `redirect_to_start_point_amp()`, `redirect_to_end_point_amp()`,
   and `amp_wp_get_requested_page_url()`. Switched all internal same-origin redirects
   from `wp_redirect()` to `wp_safe_redirect()`. Replaced loose `==` comparisons
   with strict `===` in `amp_wp_transform_post_link_to_amp()`, `amp_wp_init_json_ld()`,
   and `is_amp_excluded_by_url()`; added `true` strict flag to all `in_array()` 
   calls in `amp_version_exists()`. Refactored the empty-body if/elseif assignment-
   in-condition chain in `amp_wp_template_loader()` into an explicit early-return
   ladder that preserves the “first matching template wins” semantics including 
   the static-home-page query override and the attachment-template `prepend_attachment`
   filter removal. Renamed the private helper `_amp_wp_can_call_component_method()`
   to drop the leading underscore (visibility is already encoded in the `private`
   keyword) and removed its unused `&$args` reference parameter; the single internal
   caller was updated to match. Renamed the `$default` parameter on `amp_wp_get_option()`
   to `$default_value` to avoid the PHP reserved-keyword shadow. Filled in 19 missing
   or incomplete `@param` doc-comment lines across 13 methods. Removed three blocks
   of dead commented-out code that the surrounding live code superseded. No public-
   API behaviour change: every method signature exposed to add-ons is preserved.
 * Added: `tools/test-redirects.sh` – a curl-based smoke harness that asserts every
   plugin-issued redirect on a target site behaves the way the no-cache + Clear-
   Site-Data + 302 fix promises. Run it against staging or production before pushing
   a release: `BASE_URL=https://your-site.com ./tools/test-redirects.sh`. Covers
   eight scenarios (fresh AMP-excluded redirect, cookie-gated re-request, desktop
   UA, full chain termination, AMP-included single post, mobile auto-redirect cache
   headers, query-string preservation, paginated AMP URL) with 29 individual assertions
   including 302 status, `Cache-Control: no-cache` + `no-store`, `Clear-Site-Data:"
   cache"` presence/absence per cookie state, hardened `Set-Cookie` flags (Secure/
   HttpOnly / SameSite=Lax), Location target, and AMP markers in served bodies. 
   Configurable per site via env vars (BASE_URL, AMP_EXCLUDED_URL, AMP_INCLUDED_URL,
   NON_AMP_URL, PAGINATED_AMP_URL). Exits 0 on green, 1 on any failure with a list
   of failed assertions.

#### 1.7.4 – 2026-05-01

 * Fixed: Header and sidebar logo image no longer disappears when a logo is set 
   in AMP WP branding settings. The v1.7.2 PHPCS escaping pass wrapped the logo 
   output in `wp_kses_post()`, which strips non-standard HTML elements including`
   <amp-img>` and `<amp-anim>`. …

## Meta

 *  Version **1.7.11**
 *  Last updated **6 hours ago**
 *  Active installations **700+**
 *  WordPress version ** 5.0 or higher **
 *  Tested up to **7.0**
 *  PHP version ** 7.4 or higher **
 *  Language
 * [English (US)](https://wordpress.org/plugins/amp-wp/)
 * Tags
 * [accelerated mobile pages](https://mfe.wordpress.org/plugins/tags/accelerated-mobile-pages/)
   [amp](https://mfe.wordpress.org/plugins/tags/amp/)[google amp](https://mfe.wordpress.org/plugins/tags/google-amp/)
   [mobile](https://mfe.wordpress.org/plugins/tags/mobile/)[Page Speed](https://mfe.wordpress.org/plugins/tags/page-speed/)
 *  [Advanced View](https://mfe.wordpress.org/plugins/amp-wp/advanced/)

## Ratings

 4.5 out of 5 stars.

 *  [  45 5-star reviews     ](https://wordpress.org/support/plugin/amp-wp/reviews/?filter=5)
 *  [  3 4-star reviews     ](https://wordpress.org/support/plugin/amp-wp/reviews/?filter=4)
 *  [  1 3-star review     ](https://wordpress.org/support/plugin/amp-wp/reviews/?filter=3)
 *  [  1 2-star review     ](https://wordpress.org/support/plugin/amp-wp/reviews/?filter=2)
 *  [  5 1-star reviews     ](https://wordpress.org/support/plugin/amp-wp/reviews/?filter=1)

[Your review](https://wordpress.org/support/plugin/amp-wp/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/amp-wp/reviews/)

## Contributors

 *   [ Pixelative ](https://profiles.wordpress.org/pixelative/)
 *   [ Mohsin Rafique ](https://profiles.wordpress.org/mohsinrafique/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/amp-wp/)

## Donate

Would you like to support the advancement of this plugin?

 [ Donate to this plugin ](https://wise.com/pay/me/mohsinr301)