8

I have a site on Google Domains (www.example.com) and it's hosted with Gcloud. I followed the instructions listed here to set up SSL and https: https://cloud.google.com/appengine/docs/standard/python/securing-custom-domains-with-ssl

Basically, I just ran gcloud beta app domain-mappings update example.com --certificate-management='AUTOMATIC'

Now I can indeed access https://example.com and https://www.example.com. But I can access the unsecure http version of those domains as well.

How can I set up my Google Domain to always use https? If someone types http://example.com, I want it to go to the https site instead.

Records: My naked domain (example.com) has 4 A records and 4 AAAA records.

My www.example.com domain has 1 CNAME record with alias=www.

swagrov
  • 1,390
  • 16
  • 37
  • 1
    Please do not add your solution to your question, it should be posted as an answer instead. – GrumpyCrouton Feb 12 '18 at 20:56
  • I suggest to change the title of your question to something like this: **How to enforce HTTPS traffic to Google App Engine with custom domain?** – Ani Feb 12 '18 at 21:30

5 Answers5

4

Have you tried setting secure: always in your handlers in your app.yaml?

handlers:
- url: /youraccount/.*
  script: accounts.app
  login: required
  secure: always

always

Requests for a URL that match this handler that do not use HTTPS are automatically redirected to the HTTPS URL with the same path. Query parameters are preserved for the redirect

https://cloud.google.com/appengine/docs/standard/python/config/appref#handlers_element

Alex
  • 4,773
  • 10
  • 25
  • Thank you for your solution. It seems like it will work, but I have ```handlers: - url: /.* secure: always``` And upon deployment I get "Unknown url handler type." I have checked and the spacing on my app.yaml is valid. My site is a static site made in meteor js, if that matters. – swagrov Feb 12 '18 at 20:43
  • I have this at the top of my app yaml too: `env: flex runtime: custom threadsafe: true automatic_scaling: max_num_instances: 1` – swagrov Feb 12 '18 at 20:49
  • Sorry to spam comments, but it turns out that however gcloud parses the app.yaml, the `script` section is required, so I just put `script: UNUSED` in the field. – swagrov Feb 12 '18 at 20:54
  • How is your app working if you have `script: UNUSED`? That specifies which file has the web-app that is handling these requests. Are you on app-engine flex? If so, I think the `handlers` section is ignored and you have to look at the http header `X-Forwarded-Proto` and perform the redirect manually https://cloud.google.com/appengine/docs/flexible/python/how-requests-are-handled – Alex Feb 12 '18 at 21:03
  • I marked your answer as correct too soon then, it does not force HTTPS. I'm on env=flex and runtime=custom. I have an env_variables section with ROOT_URL=https://MYPROJECTNAME.appspot-preview.com and that works fine. There is no script to run my site. – swagrov Feb 12 '18 at 21:07
  • "There is no script to run my site." So I guess I'm a little confused, are you serving static files? What happens when someone goes to your site? – Alex Feb 12 '18 at 21:13
  • It is a static site built in Meteor. When someone goes to my site, I load my homepage by using iron-router, a meteor package. – swagrov Feb 12 '18 at 21:43
  • Is your setup similar to this? https://cloud.google.com/appengine/docs/flexible/custom-runtimes/quickstart I think your answer is in your dockerfile. If you're using nginx like they are in this example, then I think the change you need to make is in your nginx.conf. I think this should do it for you https://serverfault.com/questions/250476/how-to-force-or-redirect-to-ssl-in-nginx. ALTERNATIVELY, If all you are doing is serving static files, I'd recommend switching to app engine standard. I think it'd be simpler – Alex Feb 12 '18 at 22:05
  • My dockerfile has this (without the semicolons for readability): `FROM launcher.gcr.io/google/nodejs; RUN install_node v8.9.4; COPY . /app/; RUN (cd programs/server && npm install --unsafe-perm); CMD node main.js` – swagrov Feb 12 '18 at 22:17
  • oh! so your server-side script is node.js. So you need node.js to do the redirect then. So something like this https://stackoverflow.com/questions/7450940/automatic-https-connection-redirect-with-node-js-express (if your web app is express.js, it looks like there are several web apps for node). Is there a reason you picked a custom runtime, instead of just using app-engine flex's builtin one? https://cloud.google.com/appengine/docs/flexible/nodejs/quickstart – Alex Feb 12 '18 at 22:26
  • The site it built in Meteor, which is a framework that will create a nodejs structure for you when you build the project. One thing I have tried is `meteor add force-ssl` but my problem here is that `example.com` would redirect to `https://myproject.appspot-preview.com` which is undesired. – swagrov Feb 12 '18 at 22:36
  • Ok, can you check for `X-Forwarded-Proto != 'https'` in here http://iron-meteor.github.io/iron-router/#hooks – Alex Feb 12 '18 at 22:41
  • 1
    Thank you for that suggestion, it put me on the right path. I did `meteor add gadicohen:headers` and then my before hook has the following logic: If we're not on localhost, and the x-forward-proto is http, than just replace http with https in the current url and go to that page. It's a little sloppy but it works. `if (headers.get('x-forwarded-host') !== "localhost:3000") { if (headers.get('x-forwarded-proto') === "http") { window.location = window.location.href.replace('http', 'https') } } ` – swagrov Feb 12 '18 at 23:17
3

secure: always still works in all standard environments, but the secure option has been deprecated in all flexible environments, see documentation here or here for Node.js.

If you need this feature in your current environment, the suggested solutions require changes to your application code. Either use the custom HTTP header X-Forwarded-Proto to redirect the HTTP traffic to HTTPS, or use the HTTP Strict Transport Security response header.

Ani
  • 1,413
  • 1
  • 16
  • 28
  • Does this only apply to Python apps? My app is written in javascript using Meteor js. It is a static site. – swagrov Feb 12 '18 at 21:44
  • It also applies to Node.js apps. I have updated the answer. The suggested solutions would be the same. (I've just read the answer by GAEfan and that might be easier depending on your routing and code). However, since you mentioned that it's a static JavaScript site, maybe Firebase hosting would be more suitable? See https://firebase.google.com/docs/hosting/custom-domain which explains the combination of custom domain and SSL. – Ani Feb 12 '18 at 22:00
2

Not sure what backend language you are using, but you can brute-force to ssl by checking the request header then redirecting. Example:

if request.environ.get('HTTPS') == 'off':
    return redirect('https://www.example.com' + request.environ.get('PATH_INFO'), 301)
GAEfan
  • 10,938
  • 2
  • 15
  • 32
1

Alex's answer (see comments) put me on the right path.

First meteor add gadicohen:headers to add headers info.

In my router logic (Iron Router on Meteor) in a before hook, I check if the x-forwarded-proto is http. If so, replace http with https and go to that URL instead. I make sure I'm not on localhost too, so that I can develop the site

Router.onBeforeAction(function () {
    <some other logic>
    // Redirect http to https
    if (headers.get('x-forwarded-host') !== "localhost:3000") {
        if (headers.get('x-forwarded-proto') === "http") {
            window.location = window.location.href.replace('http', 'https')
        }
    }
});
swagrov
  • 1,390
  • 16
  • 37
1

For Java and Spring Boot with default Tomcat, following setting works fine with Google App Engine flex:

server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto
Greg Witczak
  • 1,546
  • 2
  • 24
  • 54