Blog about Full Stack Web Applications
Fixed Beta Version URL on Google App EnginePublished on 2020-02-22 Saturday
In the previous post we described how to use a non-promoted version URL of Google App Engine (GAE) to publish a beta version. This URL is different for each release, which might be problematic in some cases. In this blog post, we describe a solution for this.
Need for a Fixed URL
Although having a distinct URL fore each beta version might be handy when running multiple beta versions concurrently, there are some drawbacks if you only need one beta version:
- You need to communicate the new URL for each release.
- Some automated systems need to be reconfigured to use that URL.
- External parties might not be flexible changing the URL.
An archetypical example is OAuth 2.0, where your app redirects the user to an identity provider (IdP), with a given "redirect_url" used by the IdP to redirect the user back to your app after authentication. For security reasons, the "redirect_url" often needs to be configured at the IdP. When using "Login with Google", you need to add this each time to your OAuth settings. When using a small IdP you are integrating with, this can often not be automated.
If you want an example of such an application, please refer to https://github.com/yclybouw/app-engine-beta, where I've provided an example app using Google IdP: once logged in, you can view your Google profile (name and picture). The app is a Python Flask application running on Google App Engine Standard Environment.
Fixed URL using a Transparent Reverse Proxy
You cannot just point any domain name as CNAME to GAE, as Google has no clue to which app it should point. You can, however, set-up a transparent reverse proxy transforming all URLs to the beta version URL of GAE: it should translate all occurrences of
and vice versa:
- In the URL: in the hostname in and the query string arguments
- In all headers (including cookies)
- In the body (if not containing binary data)
You can use nginx, apache, etc., but why not using the app engine itself. On GitHub, I've implemented a reverse proxy using the Standard Environment with basic scaling (= no usage is no cost!). This is certainly slower than using nginx/apache, but for low-load, it works fine.
To use it, just clone the repo and execute:
$ gcloud app deploy --promote
A new service named beta will be deployed, reverse proxying to the most recent version of the default version. From now on, you can always reach your beta version with a fixed URL:
If you have a look at the code, you will see this is a Python app using Flask. If you look in main.py, you will see the code for the reverse proxy. There is one peculiarity, not needed if we would host the reverse proxy on our own servers: we need to delete headers added by GAE to be "transparent" for our default service:
- Headers starting with "X-". If you have your own headers starting with "X-", you need to adjust the code to keep them.
- "Forwarded" request header.
- "Content-Encoding" and "Transfer-Encoding" response headers.
Handling Server-to-Server Requests
There is one big catch: the beta service behaves as a reverse proxy between your browser and the default service, but has no impact of the requests between the default service and an external party.
Example in case of the OAuth2 flow in our example app:
- When the user logs in through Google, they receive an authorisation code from Google in the response.
- The user is redirected to our redirect_url, with the authorisation code in the query string.
- On the server side, we need to exchange the authorisation code to an access token with a server-to-server request to Google directly. Within that request we need to provide the redirect_uri that was used in step 2.
The redirect_url in step 2 will be translated by our reverse proxy, but the redirect_url in step 3 won't, because our app is aware of that reverse proxy.
To solve this, our app needs a minimal awareness of the existence of the reverse proxy. Our beta service will add a header X-OAuth-Redirect containing the original hostname (in this case beta-dot-myproject.appspot.com). The example app has been adjusted to take this header into account. If your application is doing server-to-server requests, you need a similar approach.
If you need a fixed URL to reach your beta environment, you can use a transparent reverse proxy running in Google App Engine itself to get around this issue. Only mind the sever-to-server requests.