0

I'm trying to set up a minimal nginx configuration for an API, but I cant get SSL/HTTPS to work.

Long story short: the /healthcheck endpoint works as intended, but not anything else.


I have an API application running in a Docker container on a GCE instance alongside nginx.

           Internet
               +
               |
               |
               v
+--------------+----------------+
|             GCP               |
|                               |
|    +--------------------+     |
|    |Google Load Balancer|     |
|    +---------+----------+     |
|              |                |
|              |                |
|              |                |
|              v                |
|   +----------+------------+   |
|   | Google Compute Engine |   |
|   |                       |   |
|   | +-------------------+ |   |
|   | |  Server instance  | |   |
|   | |                   | |   |
|   | | +------+  +-----+ | |   |
|   | | |Docker|  |nginx| | |   |
|   | | |      |  +-----+ | |   |
|   | | | API  |          | |   |
|   | | +------+          | |   |
|   | +-------------------+ |   |
|   +-----------------------+   |
|  ---------------------------  |
+-------------------------------+

All HTTP traffic passes through the load balancer and hits nginx. If the endpoint is /healthcheck it goes directly to the API (which returns 200 OK), while everything else should be routed back through the load balancer as HTTPS.

All HTTPS traffic should go straight to the API.

I have two server blocks in my config.
First block:

server {
    listen 80;
    server_name  my-domain.com;

    location /healthcheck {
        proxy_pass http://API_IP:8080/healthcheck;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}

Second block:

server {
    listen 443 ssl;
    server_name my-domain.com;

    location / {
        proxy_pass http://API_IP:8080$request_uri;
    }
}

With the Insomnia REST Client:
When I hit /healthcheck either as HTTP or HTTPS it works, but /users or /reviews only gives the error message Error: Couldn't resolve host name. No status code, only this error message.


Any and all help would be greatly appreciated.


Update

Output from wget -S my-domain.com/users
(Actual domain and IP is changed)

$ wget -S my-domain.com/users
--2018-08-30 14:09:08--  http://my-domain.com/users
Resolving my-domain.com (my-domain.com)... *IP REDACTED*
Connecting to my-domain.com (my-domain.com)|*IP REDACTED*|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 301 Moved Permanently
  Server: nginx/1.10.3 (Ubuntu)
  Date: Thu, 30 Aug 2018 12:09:09 GMT
  Content-Type: text/html
  Content-Length: 194
  Location: https://my-domain.com/users
  Via: 1.1 google
Location: https://my-domain.com/users [following]
--2018-08-30 14:09:09--  https://my-domain.com/users
Resolving my-domain.com (my-domain.com)... failed: Name or service not known.
wget: unable to resolve host address ‘dev.api.godlypatruljen.no’
DoTheGenes
  • 21
  • 1
  • 1
  • 6
  • Try the request with `wget -S`. My guess is that the redirect returns an invalid string. – Gerald Schneider Aug 30 '18 at 11:58
  • @GeraldSchneider - Updated question now with the output. – DoTheGenes Aug 30 '18 at 12:15
  • Well, it's impossible to say with the redacted output, but try retyping `my-domain.com` in the nginx config file. Don't copy and paste. I've seen invisible utf8-characters in the weirdest places. – Gerald Schneider Aug 30 '18 at 12:16
  • @GeraldSchneider - Well, _something_ happened at least. It's not working, but now it's just stuck in a redirection-loop; Insomnia hangs forever, while `wget -S` stops itself after 20 redirections. Oh, and the redacted IPs from the update are the same - they match the IP of the load balancer. – DoTheGenes Aug 30 '18 at 12:28

1 Answers1

0

Right.

I got a tips from a colleague which got me on the right track. It may not be the prettiest solution, but it works.

I replaced both the old server blocks with one new:

server {
    listen 80;
    listen [::]:80;
    server_name my-domain.com;

    set $redirect_to_https 0;

    if ($http_x_forwarded_proto != 'https') {
        set $redirect_to_https 1;
    }

    if ($request_uri = '/healthcheck') {
        set $redirect_to_https 0;
    }

    if ($redirect_to_https = 1) {
        return 301 https://$host$request_uri;
    }

    location / {
        proxy_pass http://$api_ip:$api_port;
    }
}

I have a few extra locations and ifs as well (in addition to what's shown here) because of some edge-cases that I need to handle (static locations on the server, but not inside the container, etc), but this example should illustrate the solution quite well.

DoTheGenes
  • 21
  • 1
  • 1
  • 6