Nginx and External Config Files for Redirects
Nginx is my favourite webserver and that's mostly because I like the config file format and how many features it brings for my needs like reverse proxy for node apps, static file serving and cache headers for SPAs and rewriting APIs into specific paths of a URL.
I'm currently working on a project that will need a few redirects to weed out typos in URLs, to deprecate a number of them (which will just yield a 410 http error code) and otherwise will mostly be static content.
This is why I thought it would be a great idea to find the most convenient way for me to create a lot of redirects in nginx and to have them as part of the project, in code version control and not on some server where they will be forgotten about until something goes wrong.
A short introduction to nginx maps
For the following two cases (the 301/302 redirect) and the 410 redirect we can use maps, which are the shortest possible way to store all of the desired URLs if we have many redirects or retired pages.
inside the default.conf
map $request_uri $rewrite_url {
include /redirects/redirects.map;
}
content of the redirects.map
/old-url1 /new-url1;
/old-url2 /new-url2;
so you can use those methods to additionally decrease your burden of writing and maintaining these config files, however we'll not be making use of maps in the example files because they make things needlessly complex ¯\_(ツ)_/¯
. If you want maps, map them yourself!
If you decide to use maps you might come across a specific error message:
nginx: [emerg] could not build map_hash, you should increase map_hash_bucket_size: 64
which just means you need to increase some memory limits in the http
block of your server:
http {
map_hash_max_size 8000;
map_hash_bucket_size 8000;
# [...]
}
External Redirects File
For the redirects we'll use a format like this (301.redirects
):
rewrite /old-url$ /new-url permanent;
and in the configuration file we will need to include it like so:
server {
listen 80 default_server;
port_in_redirect off;
location / {
root /var/www/html;
try_files $uri $uri/ /404.html;
}
location /new-url {
root /var/www/html;
try_files $uri $uri/ /404.html;
}
# including the redirects file
include ./conf.d/redirects/301.redirects;
}
With this we'll make sure that if somebody wants to access the old URL, for example: http://example.com/old-url
it will be redirected to the new url like http://example.com/new-url
.
Nginx 410 GONE
When migrating a site it's not atypical to simply retire some pages and to let search engines know that this, in fact, is intentional and not some accident, can be achieved through the 410 http status code.
To retire some pages we can include them in the same fashion as with the redirects:
# including the redirects file
include ./conf.d/redirects/301.redirects;
# include retired pages
include ./conf.d/redirects/410.redirects;
}
and then we add a file called 410.redirects
in the redirects directory. You can call these files anything you want by the way, just not anything ending in .conf
.
location = /deprecated {
return 410;
}
So when a user accesses http://example.com/deprecated
they will simply see the text:
410 Gone
nginx/1.19.1
and search engines will know to no longer send traffic to that page.
Wrapping it up (in a container)
Now, I thought it'd be much more fun to preview this in a docker container than to just write about it, so I put all the config files and the dockerfile into a small repository: https://github.com/JonathanMH/nginx-external-redirect
You can either build the docker file by building:
docker build -t nginx-external-redirect .
docker run --init -p 80:5000 -t nginx-external-redirect
or use docker-compose:
docker-compose up
and you can try the following URLs:
- http://localhost/
- http://localhost/old-url (will redirect)
- http://localhost/new-url
- http://localhost/deprecated (will 410)