Why am I getting a "495 Certificate Error" response code when proxying requests to apps using Heroku SSL?

Issue

You're getting 495 Certificate Error HTTP status code responses for requests you're proxying via a reverse proxy like Nginx. This isn't generating any logs in your application and you're unsure why it's happening.

Resolution

This error code is thrown when there's an SNI hostname mismatch. What that means is the request being routed has a Host header with a different domain than the server name that's indicated on the certificate. This can happen if you're taking requests at one domain and then proxying them to another domain that's using Heroku SSL without rewriting the Host header to match the second domain.

This can happen using any reverse proxy, but for clarity's sake we'll use Nginx to demonstrate the issue. Imagine you're accepting requests at www.example.com, but proxying all requests to /foobar to a Heroku application listening at foobar.example.com. You might have a location block that looks something like this:

set $foobar_backend foobar.example.com;

location /foobar {
  proxy_set_header        Host $host;
  proxy_set_header        X-Real-IP $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header        X-Forwarded-Proto $scheme;

  proxy_ssl_server_name on;
  proxy_ssl_name    foobar.example.com;
  proxy_ssl_protocols TLSv1.2;
  proxy_pass          https://$foobar_backend;
  proxy_read_timeout  90;
}

The critical error in the above is that the Host header is being set to $host. This just passes the original hostname through as is. Instead, it should be rewriting the Host header to foobar.example.com.