Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active April 5, 2024 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/67487d199f4cbc1968a30c60de121893 to your computer and use it in GitHub Desktop.
Save westonruter/67487d199f4cbc1968a30c60de121893 to your computer and use it in GitHub Desktop.
<?php
/**
* AMP Subdomain Paired URLs plugin file.
*
* @package Google\AMP_Subdomain_Paired_URLs
* @author Weston Ruter, Google
* @license GPL-2.0-or-later
* @copyright 2020 Google Inc.
*
* @wordpress-plugin
* Plugin Name: AMP Subdomain Paired URLs
* Plugin URI: https://gist.github.com/westonruter/67487d199f4cbc1968a30c60de121893
* Description: Demonstration of how to configure AMP plugin v2.1 to use an "amp" subdomain for paired AMP URLs (in Transitional mode or Reader mode). Depends on <a href="https://github.com/ampproject/amp-wp/pull/5558">amp-wp#5558</a>.
* Version: 0.2
* Author: Weston Ruter, Google
* Author URI: https://weston.ruter.net/
* License: GNU General Public License v2 (or later)
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Gist Plugin URI: https://gist.github.com/westonruter/67487d199f4cbc1968a30c60de121893
*/
namespace AMP_Subdomain_Paired_URLs;
add_filter(
'amp_custom_paired_url_structure',
function () {
require_once __DIR__ . '/SubdomainUrlStructure.php';
return SubdomainUrlStructure::class;
}
);
/*
* In order for this to work properly, you must define the necessary cookie constants in the wp-config.php to include
* subdomains. For example, if your site is located at https://wordpressdev.lndo.site then you may need to add this to
* add this your wp-config.php:
*
* define( 'COOKIE_DOMAIN', '.wordpressdev.lndo.site' );
* define( 'COOKIEHASH', md5( 'wordpressdev.lndo.site' ) );
* define( 'COOKIEPATH', '/' );
*
* If you do not do this, then you will not be able to preview AMP post changes or do paired browsing.
*/
/**
* Check whether the current request is for an AMP page.
*
* @return bool Is an AMP request.
*/
function is_amp_request() {
return function_exists( 'amp_is_request' ) && amp_is_request();
}
/**
* Determine whether a URL is for AMP.
*
* @param string $url URL.
* @return bool Whether the URL has the AMP subdomain.
*/
function has_amp_subdomain( $url ) {
return (bool) preg_match(
'/^amp\./',
wp_parse_url( $url, PHP_URL_HOST )
);
}
/**
* Add amp subdomain to a URL.
*
* @param string $url URL.
* @return string URL with amp subdomain.
*/
function add_amp_subdomain( $url ) {
return preg_replace( '#(?<=://)(?!amp\.)#', 'amp.', $url );
}
/**
* Remove amp subdomain from a URL.
*
* @param string $url URL which may have an amp subdomain.
* @return string URL without amp subdomain.
*/
function remove_amp_subdomain( $url ) {
return preg_replace( '#(?<=://)amp\.#', '', $url );
}
// Since the WP_HOME may have been modified so that the amp subdomain is an alias, make sure that the post canonical
// links get the subdomain removed on AMP pages.
add_filter(
'get_canonical_url',
function ( $canonical_url ) {
if ( is_amp_request() ) {
$canonical_url = remove_amp_subdomain( $canonical_url );
}
return $canonical_url;
}
);
// Make sure that validation requests to the amp subdomain are allowed.
add_filter(
'allowed_redirect_hosts',
function ( $hosts ) {
$hosts[] = wp_parse_url( add_amp_subdomain( home_url() ), PHP_URL_HOST );
return $hosts;
}
);
<?php
/**
* Class SubdomainUrlStructure.
*
* @package Google\AMP_Path_Prefix_Paired_URLs
*/
namespace AMP_Subdomain_Paired_URLs;
use AmpProject\AmpWP\PairedUrlStructure;
/**
* Descriptor for paired URL structures that use an AMP subdomain.
*/
class SubdomainUrlStructure extends PairedUrlStructure {
/**
* Add amp subdomain to a URL.
*
* @param string $url URL (or path).
* @return string URL with amp subdomain.
*/
public function add_endpoint( $url ) {
return add_amp_subdomain( $url );
}
/**
* Remove AMP path prefix from a URL.
*
* @param string $url URL (or path).
* @return string URL without amp subdomain.
*/
public function remove_endpoint( $url ) {
return remove_amp_subdomain( $url );
}
}
@sudarsha
Copy link

sudarsha commented Jan 3, 2021

Hi, this used to work well when I installed this on Nov 20. I then updated it last week (along with the amp alpha plugin), and since then, it's been delivering regular HTML files instead of the AMP version.

I tried setting it to the subfolder mode (/amp) as well as the query string mode (?amp=1). It doesn't matter. It's forwarding regular HTML files instead of AMP files.

It should be noted that I do use W3 Total Cache as my caching solution. But this problem is there even after deactivating W3TC.

Also, it should be noted that when this plugin is active, W3TC creates an amp file (and saves it to the relevant amp cache directory) that is regular HTML (not AMP).

Without this plugin, everything works as expected, with both the subdirectory as well as query string mode.

But the minute I activate this plugin, it starts sending regular HTML files. I initially thought that W3TC may be set to ignore or strip query strings. But that is not the case, as it skips the cached file (and hits the database) when I give any kind of query string (including non-sensical ones like ?yteioure=44). That suggests that W3TC is not stripping the query string and sending the html file from the cache. [In fact, even where no cache exists, it sends the HTML version]. Moreover, the fact that this issue is there even when W3TC plugin is deactivated suggests that it's not that plugin that is to blame.

There's an amp compatibility extension within W3TC. Activating and deactivating the that extension makes no difference.

Basically, the minute I install this plugin, AMP plugin no longer gives AMP version even if I append ?amp=1 to the url of the main domain.

Is it possible to obtain AMP pages any other way? (for example, by adding the amp.xx.yy subdomain to the main website?) Another thing I should probably mention here is that I used to have a plugin called 'multiple domains' installed. But I deleted it recently.

@westonruter
Copy link
Author

westonruter commented Jan 3, 2021

Which version of the plugin did you install? The pull request (ampproject/amp-wp#5558) that adds support for this is not yet merged. It should be this week. In the meantime, use the production build linked here: ampproject/amp-wp#5558 (comment).

@sudarsha
Copy link

sudarsha commented Jan 4, 2021

Hi,
version no. is 2.1.0-alpha-20201231T081928Z-45677a6

@westonruter
Copy link
Author

OK, that is correct.

As for why it isn't working… how are you able to set the "path suffix" and "query var" paired URL structures when you have the this subdomain plugin active? When you have this plugin active, all the other paired URL structures are not available because the custom one is active via the plugin.

Basically, the minute I install this plugin, AMP plugin no longer gives AMP version even if I append ?amp=1 to the url of the main domain.

That's somewhat expected. When this plugin is active then ?amp=1 is no longer the way to access the AMP version, since the subdomain is the the way to obtain the AMP version.

This subdomain plugin is intended to be a starting point to facilitate using a subdomain for AMP URLs. It's not going to be part of the plugin and so is not officially supported. When a subdomain is used, I expect it to cause problems with caching plugins that rely on the REQUEST_URI without regard for the HTTP_HOST.

Basically, what I recommend is for you to debug why this plugin is not working for you in your setup so that we can improve it to handle more use cases. If the paired URL structures are working as expected in the branch (especially query var and path suffix) then this is what the AMP plugin officially supports, and this is what we need to know for sure work on your site out of the box.

@sudarsha
Copy link

sudarsha commented Jan 4, 2021

Ok.
I did actually add amp.my.domain as one of the alternate names of my website in the nginx config. I guess the host header would then get passed on to Wordpress/PHP. From what you're saying, therefore, the plugin should internally convert any request to the amp subdomain to the parent domain+amp=1 query?

Anyway, it would have been nice if the plugin also accommodated a direct request to the parent domain with the string amp=1.

If the main website will no longer accept amp=1 query string once this paired url code is added, I can add amp subdomain as an alias of the parent domain in nginx and all 404s can then be proxied to the parent server with host=amp.xxxxx.com attached. I guess that way, it should work, as the primary server should now see that the request is coming for amp subdomain.

However, the only complicating factor in this is that Total Cache is standing 'in front' of the application, and will probably intercept the request. I can, of course, add amp.xxxx as one of the aliases of the main website in Total Cache settings (there's a specific column for aliases), but that would again only lead to the html file being returned as the plugin will assume that it can send the html file since it's just an equivalent alias.

I'll have to figure out some way to make the Total Cache plugin ignore requests to the amp subdomain and pass them directly to Wordpress unless you can make this plugin support both the subdomain as well as the query string method at the same time.

Anyway, I'll give the proxying method a try.

@westonruter
Copy link
Author

westonruter commented Jan 4, 2021

I'll have to figure out some way to make the Total Cache plugin ignore requests to the amp subdomain and pass them directly to Wordpress unless you can make this plugin support both the subdomain as well as the query string method at the same time.

Yes, you can do this. I haven't tested this, but try modifying SubdomainUrlStructure.php as follows:

diff --git a/SubdomainUrlStructure.php b/SubdomainUrlStructure.php
index 3dd4e46..4943682 100644
--- a/SubdomainUrlStructure.php
+++ b/SubdomainUrlStructure.php
@@ -21,7 +21,7 @@ class SubdomainUrlStructure extends PairedUrlStructure {
 	 * @return string URL with amp subdomain.
 	 */
 	public function add_endpoint( $url ) {
-		return add_amp_subdomain( $url );
+		return $this->paired_url->add_query_var( add_amp_subdomain( $url ) );
 	}
 
 	/**
@@ -31,6 +31,6 @@ class SubdomainUrlStructure extends PairedUrlStructure {
 	 * @return string URL without amp subdomain.
 	 */
 	public function remove_endpoint( $url ) {
-		return remove_amp_subdomain( $url );
+		return $this->paired_url->remove_query_var( remove_amp_subdomain( $url ) );
 	}
 }

@sudarsha
Copy link

sudarsha commented Jan 5, 2021

Thanks. This does work more or less fine. The only thing is, since we're adding both the amp subdomain and qeury_var to the paired url, it doesn't look very nice.
The amphtml link is in this format https://amp.xxxx.yyy/jgakjgajkldfjakfjadkl?amp=1
While that works fine, it doesn't look terribly aesthetic.
If I remove add_query_var from return $this->paired_url->add_query_var( add_amp_subdomain( $url ) ); , would it work? I'm not a coder, as you might have guessed.
Also, would I then have to do the same with in the remove_endpoint function as well?

@sudarsha
Copy link

sudarsha commented Jan 5, 2021

It does seem to me that I can remove add_query_var function from add_endpoint function, as that functionality is anyway being done by the subdomain server when it cannot find a readymade file (cached file) to supply to the requester?
It also seems to be that I should maintain the >remove_query_var function in the remove_endpoint( $url ) as, by this point, the amp=1 query string has been attached to the url by the subdomain server, in place of the amp subdomain, as part of its 302 redirection. Therefore, perhaps, when it comes to the remove_endpoint( $url ) function, I should get replace remove_query_var( remove_amp_subdomain( $url ) ) with remove_query_var($url) ????

I've removed the problem of amp being attached to both sides of the url by NOT MAKING the following change:

-		return add_amp_subdomain( $url );
+		return $this->paired_url->add_query_var( add_amp_subdomain( $url ) );

I've also changed
return $this->paired_url->remove_query_var( remove_amp_subdomain( $url ) );
to
return $this->paired_url->remove_query_var( $url );
given that there will be no subdomain when the request hits this [origin] server. The subdomain will be stripped out by the proxy during redirection.
Things seem to be working. I hope the code is fine?

@westonruter
Copy link
Author

westonruter commented Jan 5, 2021

Thanks. This does work more or less fine. The only thing is, since we're adding both the amp subdomain and qeury_var to the paired url, it doesn't look very nice.

The appearance of the AMP URL should be irrelevant.

Things seem to be working. I hope the code is fine?

It is expected that add_endpoint and remove_endpoint should do exactly the opposite operations. So if that's not done then I can't say that it will always work as intended. But if the reverse operation for you is being handled somehow elsewhere in your server stack, then good for you. The point of the custom paired URL structures is to give you the flexibility to do what you need for your particular use case. That being said, I can't get too far into the particulars of your setup.

@sudarsha
Copy link

sudarsha commented Jan 6, 2021

I've monitored the set up for more than 24 hours now. Everything is working very well. Thanks a lot for your help.

@pierlon
Copy link

pierlon commented Jan 21, 2021

Admin URLs will need to be filtered to remove the AMP subdomain as well. Some items in the AMP admin bar menu may not resolve correctly, for example.

@Wesley-Carnicle
Copy link

What if the subdomain doesn't have Wordpress installed does it still work with the main site. I know CNN did amp.cnn.com I would like to do amp.mydomain.com

@westonruter
Copy link
Author

westonruter commented Apr 5, 2024

@Wesley-Carnicle Unsure. This is only relevant when WordPress is installed on the subdomain and specifically when the subdomain is an alias of the main domain, and where the same WordPress install with the AMP plugin is on both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment