Nginx: browser rewriting http request to https when not wanted

httpsnginx

So I have an nginx config that looks like this:

## Redirects all HTTP traffic to the HTTPS host
server {
  listen *:80;
  server_name me.example.com;
  server_tokens off;
  return 301 https://me.example.com:443$request_uri;
  access_log  /var/log/nginx/access.log;
  error_log   /var/log/nginx/error.log;
}

server {
  listen *:443 ssl;
  ...
}

server {
  listen *:9080;
  location / {
    root /var/www;
    index index.html index.htm;
  }
}

The intention is to direct http traffic on port 80 to https (443). Works like a champ. The problem is that my request to port 9080 are causing my browser to switch to https and then failing (since I'm not using ssl on 9080, nor do I want to).

In Safari or Chrome: http://me.example.com:9080/index.html -> https://me.example.com:9080/index.html Can't establish secure connection.

With CURL:

curl -v http://me.example.com:9080/index.html
* Hostname was NOT found in DNS cache
*   Trying x.x.x.x...
* Connected to me.example.com (x.x.x.x) port 9080 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.37.1
> Host: me.example.com:9080
> Accept: */*
> 
< HTTP/1.1 200 OK
* Server nginx/1.4.4 is not blacklisted
< Server: nginx/1.4.4
< Date: Thu, 09 Apr 2015 18:32:02 GMT
< Content-Type: text/html
< Content-Length: 157
< Last-Modified: Thu, 09 Apr 2015 18:19:42 GMT
< Connection: keep-alive
< ETag: "5526c2be-9d"
< Accept-Ranges: bytes
< 
<html>
<head>
<title>Test Server</title>
</head>
<body>
<h3>Welcome to the Test Server!"</h3>
</body>
</html>
* Connection #0 to host me.example.com left intact

Is this a browser issue? Is there something I can do to make the browser happy?

UPDATE

In Chrome, you can remove a site from HSTS by navigating to this URL:

chrome://net-internals/#hsts

h/t to this site that has instructions for other browsers as well.

Best Answer

I'm guessing you are sending an HTTP Strict-Transport-Security (HSTS) header from the HTTPS server block.

The purpose of the HSTS header is to be bound to the domain name it was received from. It is then known as an HSTS host by the User-Agent (UA) and held in its cache for max-age seconds.

During this time further HTTP requests to the domain, or a valid subdomain if told by the includeSubDomains directive, will get through a special processing described by RFC 6797 section 8.3 :

      The UA MUST replace the URI scheme with "https" [RFC2818], and

      if the URI contains an explicit port component of "80", then
      the UA MUST convert the port component to be "443", or

      if the URI contains an explicit port component that is not
      equal to "80", the port component value MUST be preserved;
      otherwise,

      if the URI does not contain an explicit port component, the UA
      MUST NOT add one.

     NOTE:  These steps ensure that the HSTS Policy applies to HTTP
            over any TCP port of an HSTS Host.

NOTE:  In the case where an explicit port is provided (and to a
       lesser extent with subdomains), it is reasonably likely that
       there is actually an HTTP (i.e., non-secure) server running on
       the specified port and that an HTTPS request will thus fail
       (see item 6 in Appendix A ("Design Decision Notes")).

This means that if you are trying to send an HTTP request to a known HSTS host with a matching domain name (section 8.2 for the details) then as long as the HSTS host entry in the UA cache has not expired, HTTP traffic will transparently switch to HTTPS either :

  • on port 443 if the HTTP port was 80 (explicitely or implicitely) in the target URI
  • on the same port otherwise