Naked Domain redirect to www with kubernetes nginx


Here at, one of our customers had the following setup:

  • DNS records managed in GoDaddy
  • 3 DNS A records with “@” name and with Google IPs value (,, Usually these are set when Google Suite or Google Cloud Service is configured for a Domain (more information here). These are the IP´s you would get if you do a nslookup
  • website was actually in “www” subdomain, with a CNAME record pointing to a url from CDN provider. This means, when you access you are actually accessing something like or whatever URL your CDN provider gives. Here if you do a nslookup you would get something like this:
Non-authoritative answer: canonical name =

Noticed canonical name ? That’s actually CNAME

So, if final users try to access the site with any possible combination:


It should be redirected to (this would technically be possibility #4).


Option #1 works, since there is a 301 redirection in place (this comes from Google, notice “Server: ghs”):

#curl -I
HTTP/1.1 301 Moved Permanently
Date: Wed, 15 Aug 2018 18:27:55 GMT
Content-Type: text/html; charset=UTF-8
Server: ghs
Content-Length: 234
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN

Option #2 works, since subdomain www actually takes you to CDN, and in there we have an enforcement to redirect you to https if you are coming from http .

But the problem is in #3:

#curl -I
curl: (7) Failed to connect to port 443: Operation timed out

If you try to browse it, you would get site can't be reached error.


Google / G-Suite / Google Domain

Since the A records are currently pointing to Google IPs, our first option was to change settings there, to update that 301 Redirect rule for HTTPS, and upload our SSL Certificate to enable 443 port.

Unfortunately we found no way of doing that, so we analyze the following alternative.


Currently GoDaddy does not offer ALIAS DNS record, and we cannot setup CNAME for Naked Domain because of RFC1034 (section 3.6.2).

An ALIAS record is a virtual record type, which we could say is kinda a mixture of A and CNAME, where following our example, we could add a record for the naked domain to and if we do a nslookup we would see the ip from . So its like having an A record, that automatically updates the IP value.

However, they do offer a Forwarding section:

While applying this, the A records get removed and only 1 A record is added, with an IP from GoDaddy. But we get almost same results than before:

#curl -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.12.2
Date: Thu, 16 Aug 2018 18:10:16 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Location: https://www.example
#curl -I
curl: (7) Failed to connect to port 443: Operation timed out

Note: After several support contact with GoDaddy, they are not able to add our SSL Cert (which we bought it from them), which would solve the issues. And yes, they use a nginx as well.

A Record with IP from CDN

Technically, what we could do, is create an A record with the IP we got from CDN Provider, This might work, but it’s not recommended at all…because we don’t have control at all of that IP. If our CDN provider changes that IP, we will have to manually update the A Record, with downtime until we do it and propagates.


We ended up deploying our own nginx server in our GCP Kubernetes Cluster, with a STATIC IP Address we reserve in GCP, which we have control and we know it won’t change.

Note: You could opt for the Cloud Provider/Source Repository/CI of your choice

We will try to sum up the steps we did to deploy the solution:

  1. Get the latest helm chart for nginx-ingress (we like to use Helm, as you can see in the chart.yaml file the source is kubernetes/ingress-nginx)
  2. We use Bitbucket as source repository with Bitbucket pipelines as CI, so we created a new repository for this.
  3. We reserve an static IP in GCP that we are going to use in the nginx controller service (LoadBalancer type). IMPORTANT: This IP needs to be regional, and in same region than the GKE Cluster.
  4. Your helm customization file should look something like this:
externalTrafficPolicy: Local
replicaCount: 2
enabled: true
annotations: "false" |
if ($http_x_forwarded_proto != 'https') {
return 301 https://www.$host$request_uri;
- hosts:
secretName: secret-with-tls

- host:
- backend:
serviceName: default-backend
servicePort: 80
path: /

Being the IP reserved in step #4. You need to specify either a rule or backend, even though it will not route it there because the redirection happens before.

There are many other options/features you could enable/configure. Either if you want a different redirection, or if you don’t want to redirect maintaining the request_uri …anything that matches your needs!

5. Once this is successfully deployed, you could test functionality using curl:

curl -Ik -H 'Host:'
HTTP/2 301
server: nginx/1.13.12
date: Thu, 16 Aug 2018 18:24:00 GMT
content-type: text/html
content-length: 186

And also validate, that is including the request_uri :

curl -Ik -H 'Host:'
HTTP/2 301
server: nginx/1.13.12
date: Thu, 16 Aug 2018 18:24:00 GMT
content-type: text/html
content-length: 186

7. Once you finished validation, you are ready to update the A record in your DNS, using the IP we got from step #4.

8. Once DNS propagation is over (we recommend setting a small value, in GoDaddy you could set it to 600 seconds )

And thats it!

Hope you found this article useful, please feel free to comment