Create a local HTTPS proxy server
Hold on Cowboy
This blog post is pretty old. Be careful with the information you find in here. It's likely dead, dying, or wildly inaccurate.
In recent months I’ve been working to add Apple Pay for Web to a major clothing retailer. One of the requirements for Apple Pay for Web is that the connection must be over HTTPS. Most of the time when I’m developing locally, I do not use HTTPS. Local, meaning the application code is running on my laptop. In most cases, HTTPS is just run in staging and production environments and not handled directly by your app code.
So this posed a problem. I didn’t want to work on the server where it provides an HTTPS connection, that’s a pain. Also, if you’re not familiar with HTTPS servers, you need to have a valid certificate and more. So this solution requires a few steps to get working, but once it does, it works nicely.
TL;DR
- Create a fake SSL certificate (e.g. www.example.com)
- Create a docker container that uses Nginx to proxy localhost:443 => 172.16.123.1:3000
- Override your local /etc/hosts file
- Start your app on localhost:3000
- Hit your app in the browser at https://www.example.com
Creating a fake SSL certificate
It’s most likely not a good idea to use your production SSL certificate and key when doing local development, so you’ll want to create a fake version.
It’s entirely possible to purchase legitimate SSL certifates and use them, but this is not always possible, and it’s free to make your own
Update this code and run it on a Mac or Linux machine
sudo openssl req -x509 -sha256 -newkey rsa:2048 -keyout cert.key -out cert.pem -days 1024 -nodes -subj '/CN=www.example.com'
You now have files called cert.key
and cert.pem
for a certificate that is valid for the next 10 years.
Caveat: Your browser will not like this certificate, but since you trust it, you can override the browser to accept the certificate and not complain.
Save those files, we’ll use them in just a minute
Create a docker container
Now we want to create a docker container that will use our certificate and proxy all requests to our app running on port 3000. We will use docker compose to accomplish the configuration of the container.
Create a directory
First, you’ll want to create a directory where you can hold the docker compose yaml file, the nginx configuration, and the ssl certificates. Mine looks like this:
.
├── docker-compose.yml
├── proxy.conf
└── ssl
├── cert.key
└── cert.pem
The Docker Compose file
The docker-compose.yml
file is really pretty short. Here it is.
version: '2'
services:
nginx:
image: nginx:stable-alpine
volumes:
- ./proxy.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/nginx/ssl
ports:
- 443:443
As you can see:
- We pull down the nginx image using the small linux alpine version.
- Then we mount some volumes inside the container, the
proxy.conf
file and thessl
folder that holds our certs. - Next the configuration tells the container to listen on port 443 and forward internal to port 443
The Nginx configuration
Now lets take a look at our proxy.conf
file.
server {
listen 443 ssl;
server_name localhost;
ssl on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate ssl/cert.pem;
ssl_certificate_key ssl/cert.key;
location / {
proxy_pass http://host.docker.internal:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-HTTPS 'True';
}
}
In here you’ll notice a few items of interest:
- We specify our cert and key as living in the
ssl/*
folder we mounted in our docker-compose.yml file - We are proxy passing all traffic to
host.docker.internal:3000
, this will resolve to your localhost:3000 on your machine. - The rest is just standard configuration for setting up Nginx as a proxy.
Start your docker container
Inside your folder that has the docker-compose.yml
file, all you need to do is run docker-compose up -d
. That will start your HTTPS proxy and put it in the background.
Check if it started successfully by running docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------
ssltesting_nginx_1 nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 80/tcp
Override your local /etc/hosts file
Now we need to tell our computer that www.example.com
is not something it should ask DNS about, but can get it’s answer right from /etc/hosts
. Just so you understand, when your browser needs to resolve an address to a domain name, it will first look in /etc/hosts
, then ask DNS.
So we just need to add this to our /etc/hosts
file.
127.0.0.1 www.example.com
This pretty easy, if you are going to be flipping this on/off I would suggest an app like Gas Mask or HostBuddy.
Start your application on port 3000
This is something you’ll have to figure out yourself.
Hit you application in the browser.
Now it’s time to open the application in the browser and test the whole thing. Go ahead and open up https://www.example.com in the browser. If we’ve done everything right, we should see a warning
What the?!
![Chrome warning](/images/Screen Shot 2016-10-03 at 9.00.31 PM.png)
This is a good thing, Chrome doesn’t trust our self signed certification, so now we just tell Chrome to trust it by clicking Advanced > Proceed to www.example.com (unsafe))
If you get a 502 Bad Gateway it means that Nginx cannot reach your app on port 3000. Most likely because there is a problem with your container talking to your host. Remember I said it’s not as easy as it seems :(
Conclusion
So now you have a local HTTPS server that imitates a production one. It’s now possible to do any type of development locally in your app that might require an HTTPS connection, like Apple Pay for the Web.
If your company needs help implementing Apple Pay for the Web, please contact me. I contract out my services and can have your site accepting Apple Pay payments in short order.