I am currently in the process of migrating a bunch of sites on this machine from Apache to nginx. Rather than take everything down and migrate it all at once, I wanted to do this incrementally. But that raises a question: how do you incrementally migrate site configs from one to the other on the same machine, since both servers will need to be running and listening on ports 80 and 443?
The solution I came up with was to move Apache to different ports (8080 and 4443) and to set the default nginx config to be a reverse proxy!
First thing is to run a sed
command to batch change all the port 80s in
virtual host configs to 8080s.
$ sed -i -- 's/:80/:8080/g' *.conf
Then, edit /etc/apache2/ports.conf
and changed the Listen
directives from 80
to 8080. Then restart. You should now be able to hit all your existing sites on
port 8080.
Next, install nginx.
$ sudo apt install nginx
Then, edit the /etc/sites-enabled/default
file. Start by removing all the
contents.
From there, it’s like any standard nginx reverse proxy out there with one small
difference: you have to pass the Host
header back to Apache so that it can do
the name-based virtual host lookups.
server {
listen 80 default_server;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
Restart nginx, and any unsecured sites you have should now be working.
Becuse this is the default config, adding more specific configs will override this, allowing you to start moving your configs over. nginx will use the local config if it exists, or pass it to apache if it doesn’t.
But What About HTTPS?
For HTTPS, things are more complex.
Basically what we are doing now is creating a straight up TCP reverse proxy that will pass SSL through from Apache to the client, so we will need to leverage nginx’s stream support. But, we need to check to be sure that we can’t serve it natively from nginx before passing it off to Apache.
First, similar to above, convert all your old Apache configs to listen on 4443:
$ sed -i -- 's/:443/:4443/g' *.conf
Then, edit /etc/apache2/ports.conf
and changed the Listen
directives from
443 to 4443. Then restart. You should now be able to hit all your existing sites
on port 4443.
Create a file called /etc/nginx/modules-enabled/999-default.conf
and add the
following config to it:
stream {
map $ssl_preread_server_name $selected_upstream {
default apache;
}
upstream apache {
server 127.0.0.1:4443;
}
upstream nginx {
server 127.0.0.1:4442;
}
server {
listen 443;
proxy_pass $selected_upstream;
ssl_preread on;
}
}
Now restart nginx, and you should be able to hit all your sites on either 80 or 443, HTTP or HTTPS and have everything work.
Now this is where things get a little hairy. As you are migrating configs, for SSL, you will need to have your new nginx configs listen in port 4442, and then update the map in the above. For example:
map $ssl_preread_server_name $selected_upstream {
robpeck.com nginx;
default apache;
}
Will now send SNI requests for robpeck.com to nginx on port 4442, where our
config will take over and return the correct certificate and files. And once
your migration is complete, you’ll just need to remove the 999-default.conf
file and convert all your nginx configs that listen on 4442 to listen on 443.