Multi-page URL Fallback (index.html & subpage.html) - google-app-engine

I'm trying to deploy a multi-page Vue.js app using google apps python engine.
In order for the Vue.js application to work I need all URL's to fallback so the vue javascript router can take over.
Index.html links:
/foo
/bar
/foo/bar
Subpage.html links:
/subpage/foo
/subpage/bar
/subpage/foo/bar
In my current configuration if I remove the wildcard from the index, then the subpage is accessible, but the index links no longer work. Is it possible to achieve multiple fallbacks, first to '/' and second to '/subpage/'?
I've included my app.yaml which, in my opinion should work as written but doesn't.
runtime: python27
api_version: 1
threadsafe: true
handlers:
# Fonts and images
- url: /(.+\.(eot|otf|tt[cf]|woff2?|cur|gif|ico|jpe?g|png|svgz?|webp))
static_files: dist/\1
upload: dist/(.+\.(eot|otf|tt[cf]|woff2?|cur|gif|ico|jpe?g|png|svgz?|webp))
secure: always
http_headers:
Access-Control-Allow-Origin: "*"
# CSS, Javascript, text and other file types
- url: /(.+\.(css|js|xml|txt|map))
static_files: dist/\1
upload: dist/(.+\.(css|js|xml|txt|map))
expiration: "10m"
secure: always
# HTML pages
- url: /(.+\.html)
static_files: dist/\1
upload: dist/(.+\.html)
expiration: '10m'
secure: always
http_headers:
X-UA-Compatible: 'IE=edge'
# Index entry point
- url: /.*
static_files: dist/index.html
upload: dist/index.html
expiration: '10m'
secure: always
http_headers:
X-UA-Compatible: 'IE=edge'
# Subpage entry point
- url: /subpage/.*
static_files: dist/subpage.html
upload: dist/subpage.html
expiration: '10m'
secure: always
http_headers:
X-UA-Compatible: 'IE=edge'
skip_files:
- ^(.*/)?app\.yaml
- ^(.*/)?app\.yml
- ^(.*/)?#.*#
- ^(.*/)?.*~
- ^(.*/)?.*/RCS/.*
- ^(.*/)?\..*
- ^(.*/)?tests$
- ^(.*/)?test$
- ^test/(.*/)?
- ^COPYING.LESSER
- ^README\..*
- \.gitignore
- ^\.git/.*
- \.*\.lint$
- ^node_modules/(.*/)?
- public/*
- src/*

Your problem is that - url: /.* catches EVERYTHING not yet caught. So, the handlers never get to /subpage/.*
Move the /subpage/.* handler above the wildcard - url: /.*:
# Subpage entry point
- url: /subpage/.*
static_files: dist/subpage.html
upload: dist/subpage.html
expiration: '10m'
secure: always
http_headers:
X-UA-Compatible: 'IE=edge'
# Index entry point
- url: /.*
static_files: dist/index.html
upload: dist/index.html
expiration: '10m'
secure: always
http_headers:
X-UA-Compatible: 'IE=edge'

Related

App Engine app.yaml variable subdirectories

Here's my current app.yaml:
runtime: nodejs10
service: default
instance_class: F1
automatic_scaling:
min_instances: 0
max_instances: 2
handlers:
# Serve extension from where they were requested
- url: /(.*\.(css|ico|js|png|jpg|gif|mp4|txt|xml|json))$
static_files: dist/apps/my-app/browser/\1
upload: dist/apps/my-app/browser/.*\.(css|ico|js|png|jpg|gif|mp4|txt|xml|json)$
secure: always
expiration: "365d"
# Handle blog SEO routes
- url: /blog
static_files: dist/apps/my-app/browser/blog/index.html
upload: dist/apps/my-app/browser/blog/index.html
secure: always
expiration: "0s"
- url: /blog/introducing-my-app
static_files: dist/apps/my-app/browser/blog/introducing-my-app/index.html
upload: dist/apps/my-app/browser/blog/introducing-my-app/index.html
secure: always
expiration: "0s"
- url: /blog/added-international-support
static_files: dist/apps/my-app/browser/blog/added-international-support/index.html
upload: dist/apps/my-app/browser/blog/added-international-support/index.html
secure: always
expiration: "0s"
- url: /blog/new-blog-entry
static_files: dist/apps/my-app/browser/blog/new-blog-entry/index.html
upload: dist/apps/my-app/browser/blog/new-blog-entry/index.html
secure: always
expiration: "0s"
# Handle default index.html
- url: /(.*)
static_files: dist/apps/my-app/browser/index.html
upload: dist/apps/my-app/browser/index.html
secure: always
expiration: "0s"
How can I combine the rules under "Handle blog SEO routes" in to one rule? Updating app.yaml with every blog entry is not scalable.
This structure is generated from an Angular Universal Prerender project.
I have tried a few combinations with no luck:
- url: /blog/(.*)
static_files: dist/apps/my-app/browser/blog/\1/index.html
upload: dist/apps/my-app/browser/blog/\1/index.html
secure: always
expiration: "0s"
I think your last try is very close to the actual solution. Please, simply adjust the upload value:
- url: /blog/(.*)
static_files: dist/apps/my-app/browser/\1/index.html
upload: dist/apps/my-app/browser/(.*)/index.html
secure: always
expiration: "0s"
As you can see in the Google Cloud documentation when describing the upload key, you need to provide regex placeholders and not back references in its definition.
Please, consider review similar question here in SO like this one or this other.

Configure Google App Engine yaml file to handle 404 Error

After the deployment of my application on Google App Engine every things works like a charm , I can access all pages but when I refresh I receive a 404 error
Example : when refreching https://my-app...appspot.com/create-ad throw 404 not found
I tried
Angular 6 routes not found on Google App Engine
and
How to configure Google App Engine yaml file to handle 404 Error
but same result
This on my app.yml
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /
static_files: dist/index.html
upload: dist/index.html
- url: /
static_dir: dist
- url: /.*
static_files: dist/index.html
upload: dist/index.html
skip_files:
- ^.*node_modules(/.*)?
- ^.*json_data(/.*)?
- ^.*e2e(/.*)?
and also tried this app.yml config to redirect all url to index.html
runtime: python27
api_version: 1
threadsafe: false
service: frontend-accept
handlers:
- url: /
static_files: dist/index.html
upload: dist/index.html
- url: /
static_dir: dist
- url: /.*
script: main.py
skip_files:
- ^.*node_modules(/.*)?
- ^.*json_data(/.*)?
- ^.*e2e(/.*)?
this is my main.py
import webapp2
app = webapp2.WSGIApplication()
class RedirectToHome(webapp2.RequestHandler):
def get(self, path):
self.redirect('/dist/index.html')
routes = [
RedirectRoute('/<path:.*>', RedirectToHome),
]
for r in routes:
app.router.add(r)
But always get 404 when refreshing the page
Any help? Thanks
The reason of the 404 HTTP error code is due to this handlers:
- url:
static_dir: dist
As stated in the Google App Engine official documentation, using static_dir: dist causes that all URLs beginning with the / pattern are treated as paths to static files in the /dist directory, so for example every time you call the URL /whatever the application will look for the file /dist/whatever, since it doesn’t exist you get the 404 error.
I believe that the following code will work as you are expecting:
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /
static_files: dist/index.html
upload: dist/index.html
- url: /dist/index.html
static_files: dist/index.html
upload: dist/index.html
- url: /.*
script: main.app
skip_files:
- ^.*node_modules(/.*)?
- ^.*json_data(/.*)?
- ^.*e2e(/.*)?
The handler for the / endpoint will serve the dist/index.html file, as well as the /dist/index.html endpoint.
Handlers are checked sequentially and if none of the above handlers have been called then any URL matching the pattern /.* ( which is all of them) will call the main.app script (this will basically override the 404 error message).
This script is redirecting you to the /dist/index.html endpoint, so this is the reason why it needs to be handled in the yaml file.
As a final point I had to import webapp2_extras.routes to use RedirectRoute in the main.py.
finally the solution was to configure the app.yml properly
runtime: python27
api_version: 1
threadsafe: true
skip_files:
- ^(?!dist) # Skip any files not in the dist folder
handlers:
- url: /((?:runtime|main|polyfills|styles|vendor)\.[a-z0-9]+\.js)
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /((?:runtime|main|polyfills|styles|vendor)\.[a-z0-9]+\.js\.map)
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /(styles\.[a-z0-9]+\.css)
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /((?:assets|docs)/.*|favicon\.ico)
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /(manifest\.json|ngsw\.json|ngsw-worker\.js|safety-worker\.js|worker-basic\.min\.js|ngsw_worker\.es6\.js\.map)
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /(.*\.woff)
mime_type: application/x-font-woff
secure: always
redirect_http_response_code: 301
static_files: dist/browser/\1
upload: dist/browser/.*
- url: /.*
secure: always
redirect_http_response_code: 301
static_files: dist/browser/index.html
upload: dist/browser/index\.html
http_headers:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: DENY

Google App Engine Static Website Handler Wildcard Routes

I have a React.js website using create-react-app and just moved from Firebase Hosting to Google App Engine Standard Environment.
With Firebase Hosting, I could create routes like https://example.com/route or https://example.com/newRoute/123 and Firebase would know to serve the index.html for any of these routes.
I want to wildcard any route of our application to use the index.html while excluding .js and .css files. If I use only the wildcard /(.*) then the request for our main.js file also resolves to index.html.
Here is our handler config
handlers:
- url: /
secure: always
application_readable: false
static_files: build/index.html
require_matching_file: false
upload: build/index.html
- url: /login
secure: always
application_readable: false
static_files: build/index.html
require_matching_file: false
upload: build/index.html
- url: '/(.*)'
secure: always
application_readable: false
static_files: "build/\\1"
require_matching_file: false
upload: 'build/.*'
In the current configuration, every route I create must be registered as a handler. I was hoping to find a solution where all current routes and future routes can be handled by a wildcard.
The order of the handlers matters, the 1st one with a matching pattern wins.
So to achieve what you want you could handle the exceptions first, before "wildcarding" the index.html. Something along these lines (I assumed the .css and .js files are also relative to the build dir):
handlers:
- url: /(.*\.css)$
secure: always
static_files: build/\1
upload: build/.*\.css$
- url: /(.*\.js)$
secure: always
static_files: build/\1
upload: build/.*\.js$
# continue similarly the other static assets
# or maybe try a more generic one covering several of them:
- url: /(.*\.(js|css|png|jpg|svg))$
secure: always
static_files: build/\1
upload: build/.*\.(js|css|png|jpg|svg)$
# wildcard everything else, serving index.html
- url: '/(.*)'
secure: always
static_files: build/index.html
upload: build/index.html
Side note: for readability I also dropped the require_matching_file (no such thing in GAE) and application_readable (by default it is false).
The answer from Dan still does it.
The only addition I had to implement was for fonts extension in the 3rd handler.
As a FYI, I am using this config for a Vue 3 app and the default build settings.
Final app.yaml for me:
handlers:
- url: /(.*\.css)$
secure: always
static_files: build/\1
upload: build/.*\.css$
- url: /(.*\.js)$
secure: always
static_files: build/\1
upload: build/.*\.js$
# continue similarly the other static assets
# or maybe try a more generic one covering several of them:
- url: /(.*\.(js|css|png|jpg|svg|woff|woff2|ttf))$
secure: always
static_files: dist/\1
upload: dist/.*\.(js|css|png|jpg|svg|woff|woff2|ttf)$
# wildcard everything else, serving index.html
- url: '/(.*)'
secure: always
static_files: build/index.html
upload: build/index.html

How do I make Angular 2 routing work with App Engine on page refresh?

I am trying to make an Angular 2 app running on App Engine Standard Environment. It works with the following app.yaml configuration when navigating within the app:
handlers:
- url: /api/.*
script: _go_app
- url: (.*)/
static_files: static\1/index.html
upload: static
- url: (.*)
static_files: static\1
upload: static
I can click on a link from / to /clients or /clients/234234 and it works fine.
However if I refresh the browser in a non base path e.g. http://myapp.appspot.com/clients/234234 then I get a 404 error. I guess I need to serve my index.html from all paths which is what I thought (.*)/ and (.*) would do.
How can I set up my handlers/app so I can use HTML5 routing and not let this happen?
I have a bunch of static files that need to be served so I added their mappings first. I also (most importantly) changed the way index.html was served:
handlers:
- url: /api/.*
script: _go_app
- url: /(.*\.svg)
static_files: static/\1
upload: static/(.*\.svg)
- url: /(.*\.js)
static_files: static/\1
upload: static/(.*\.js)
- url: /(.*\.map)
mime_type: application/octet-stream
static_files: static/\1
upload: static/(.*\.map)
- url: (.*)/
static_files: static/index.html
upload: static
- url: (.*)
static_files: static/index.html
upload: static

Google App Engine url path lost with www subdomain

I just moved a couple of static websites onto Google's App Engine, where, if you want to use your own domain, a www subdomain is required. Both domains are routing correctly, with the exception that one's full url path doesn't get passed on to the subdomain unless a www is typed or present in the link; without www, pages redirect home.
The working site—either link will work:
http://www.synth.tk/daw/
http://synth.tk/daw/
The problem site—only the first link will work; the second redirects to the homepage:
http://www.carolyncaton.com/photos/
http://carolyncaton.com/photos/
Both domains are set up with FreeDNS using the following host records as per Google's instructions:
# | A | 216.239.32.21
# | A | 216.239.34.21
# | A | 216.239.36.21
# | A | 216.239.38.21
www | CNAME | ghs.google.com
And both are using the following app.yaml file:
application: app-name-here
version: 1
runtime: python
api_version: 1
default_expiration: "30d"
handlers:
- url: /(.*\.css)
mime_type: text/css
static_files: static/\1
upload: static/(.*\.css)
- url: /(.*\.html)
mime_type: text/html
static_files: static/\1
upload: static/(.*\.html)
expiration: "1h"
- url: /(.*\.js)
mime_type: text/javascript
static_files: static/\1
upload: static/(.*\.js)
- url: /(.*\.eot)
mime_type: application/vnd.ms-fontobject
static_files: static/\1
upload: static/(.*\.eot)
- url: /(.*\.(svg|svgz))
mime_type: images/svg+xml
static_files: static/\1
upload: static/(.*\.(svg|svgz))
- url: /(.*\.ttf)
mime_type: font/truetype
static_files: static/\1
upload: static/(.*\.ttf)
- url: /(.*\.woff)
mime_type: application/x-font-woff
static_files: static/\1
upload: static/(.*\.woff)
- url: /(.*\.pdf)
mime_type: application/pdf
static_files: static/\1
upload: static/(.*\.pdf)
# image files
- url: /(.*\.(bmp|gif|ico|jpeg|jpg|png))
static_files: static/\1
upload: static/(.*\.(bmp|gif|ico|jpeg|jpg|png))
# index files
- url: /(.+)/
static_files: static/\1/index.html
upload: static/(.+)/index.html
expiration: "15m"
- url: /(.+)
static_files: static/\1/index.html
upload: static/(.+)/index.html
expiration: "15m"
# site root
- url: /
static_files: static/index.html
upload: static/index.html
expiration: "15m"
Using developer console in Chrome (F12, click the "preserve log upon navigation" button) you can see that http://carolyncaton.com/photos/ gets a 302 response to http://www.carolyncaton.com/. Since App Engine doesn't support naked (apex) domains, the problem must be elsewhere. I suggest you check FreeDNS for a setting like 'preserve path'.
Google Appengine does not support 'naked domains', see
http://code.google.com/intl/en/appengine/kb/general.html#naked_domain
Both sites seem to work now. Perhaps it just took a while for the nameserver updates to propagate.

Resources