This document is a WORK IN PROGRESS.
This is just a quick personal cheat sheet: treat its contents with caution!
Nginx¶
Nginx is a robust, small, high performance web server and reverse proxy server.
Reference(s)
Table of contents¶
- Install
- Default Nginx config
- Hello world example
- Reverse proxy
- Hosting multiple websites
- Password protection
- HTTPS digital certificate with Certbot
Install¶
Install nginx
Check nginx
installation
Default Nginx config¶
Warning
The configuration of Nginx depends on the application one want to deploy with. The configuration of Nginx will therefore be addressed on a case by case basis: see the cheat sheet of the application you want to deploy (e.g. Nextcloud or e.g. Radicale).
For example here is my default /etc/nginx/nginx.conf
configuration whit nginx
v1.20.1
(on
Arch Linux):
Default Nginx config
$ sudo vi /etc/nginx/nginx.conf
> #user http;
> worker_processes 1;
>
> #error_log logs/error.log;
> #error_log logs/error.log notice;
> #error_log logs/error.log info;
>
> #pid logs/nginx.pid;
>
>
> events {
> worker_connections 1024;
> }
>
>
> http {
> include mime.types;
> default_type application/octet-stream;
>
> types_hash_max_size 4096; #
> server_names_hash_bucket_size 128; # see https://wiki.archlinux.org/title/nginx#Warning:_Could_not_build_optimal_types_hash
>
> #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
> # '$status $body_bytes_sent "$http_referer" '
> # '"$http_user_agent" "$http_x_forwarded_for"';
>
> #access_log logs/access.log main;
>
> sendfile on;
> #tcp_nopush on;
>
> #keepalive_timeout 0;
> keepalive_timeout 65;
>
> #gzip on;
>
> server {
> listen 80;
> server_name localhost;
>
> #charset koi8-r;
>
> #access_log logs/host.access.log main;
>
> location / {
> root /usr/share/nginx/html;
> index index.html index.htm;
> }
>
> #error_page 404 /404.html;
>
> # redirect server error pages to the static page /50x.html
> #
> error_page 500 502 503 504 /50x.html;
> location = /50x.html {
> root /usr/share/nginx/html;
> }
>
> # proxy the PHP scripts to Apache listening on 127.0.0.1:80
> #
> #location ~ \.php$ {
> # proxy_pass http://127.0.0.1;
> #}
>
> # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
> #
> #location ~ \.php$ {
> # root html;
> # fastcgi_pass 127.0.0.1:9000;
> # fastcgi_index index.php;
> # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
> # include fastcgi_params;
> #}
>
> # deny access to .htaccess files, if Apache's document root
> # concurs with nginx's one
> #
> #location ~ /\.ht {
> # deny all;
> #}
> }
>
>
> # another virtual host using mix of IP-, name-, and port-based configuration
> #
> #server {
> # listen 8000;
> # listen somename:8080;
> # server_name somename alias another.alias;
>
> # location / {
> # root html;
> # index index.html index.htm;
> # }
> #}
>
>
> # HTTPS server
> #
> #server {
> # listen 443 ssl;
> # server_name localhost;
>
> # ssl_certificate cert.pem;
> # ssl_certificate_key cert.key;
>
> # ssl_session_cache shared:SSL:1m;
> # ssl_session_timeout 5m;
>
> # ssl_ciphers HIGH:!aNULL:!MD5;
> # ssl_prefer_server_ciphers on;
>
> # location / {
> # root html;
> # index index.html index.htm;
> # }
> #}
>
> }
And here is how I safely modified it:
Modified Nginx config
$ sudo vi /etc/nginx/nginx.conf
> #user http;
> worker_processes 1;
>
> #error_log logs/error.log;
> #error_log logs/error.log notice;
> #error_log logs/error.log info;
>
> #pid logs/nginx.pid;
>
>
> events {
> worker_connections 1024;
> }
>
>
> http {
> include mime.types;
> default_type application/octet-stream;
>
> types_hash_max_size 4096; #
> server_names_hash_bucket_size 128; # see https://wiki.archlinux.org/title/nginx#Warning:_Could_not_build_optimal_types_hash
>
> #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
> # '$status $body_bytes_sent "$http_referer" '
> # '"$http_user_agent" "$http_x_forwarded_for"';
>
> #access_log logs/access.log main;
>
> sendfile on;
> #tcp_nopush on;
>
> #keepalive_timeout 0;
> keepalive_timeout 65;
>
> #gzip on;
>
> include /etc/nginx/conf.d/default_nginx.conf;
> }
# mkdir -p /etc/nginx/conf.d
# vi /etc/nginx/conf.d/default_nginx.conf
> server {
> listen 80;
> server_name localhost;
>
> #charset koi8-r;
>
> #access_log logs/host.access.log main;
>
> location / {
> root /usr/share/nginx/html;
> index index.html index.htm;
> }
>
> #error_page 404 /404.html;
>
> # redirect server error pages to the static page /50x.html
> #
> error_page 500 502 503 504 /50x.html;
> location = /50x.html {
> root /usr/share/nginx/html;
> }
>
> # proxy the PHP scripts to Apache listening on 127.0.0.1:80
> #
> #location ~ \.php$ {
> # proxy_pass http://127.0.0.1;
> #}
>
> # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
> #
> #location ~ \.php$ {
> # root html;
> # fastcgi_pass 127.0.0.1:9000;
> # fastcgi_index index.php;
> # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
> # include fastcgi_params;
> #}
>
> # deny access to .htaccess files, if Apache's document root
> # concurs with nginx's one
> #
> #location ~ /\.ht {
> # deny all;
> #}
> }
>
>
> # another virtual host using mix of IP-, name-, and port-based configuration
> #
> #server {
> # listen 8000;
> # listen somename:8080;
> # server_name somename alias another.alias;
>
> # location / {
> # root html;
> # index index.html index.htm;
> # }
> #}
>
>
> # HTTPS server
> #
> #server {
> # listen 443 ssl;
> # server_name localhost;
>
> # ssl_certificate cert.pem;
> # ssl_certificate_key cert.key;
>
> # ssl_session_cache shared:SSL:1m;
> # ssl_session_timeout 5m;
>
> # ssl_ciphers HIGH:!aNULL:!MD5;
> # ssl_prefer_server_ciphers on;
>
> # location / {
> # root html;
> # index index.html index.htm;
> # }
> #}
The modified version results in the exact same configuration, but I find it way more convenient. Let's see how convenient with the below simple "Hello world!" example.
Check the Nginx config¶
Simply run:
Service¶
After configuring Nginx, make sure to start the nginx
service:
And don't forget to enable the nginx
service on boot:
Tip
You can find more nginx
service related commands
here (note that the nginx
service name simply is nginx
).
Hello world example¶
# mkdir -p /var/www/hello-world
# echo "Hello world!" > /var/www/hello-world/index.html
# vi /etc/nginx/nginx.conf
> ...
>
~ > #include /etc/nginx/conf.d/default_nginx.conf;
+ > include /etc/nginx/conf.d/hello_world.conf;
> }
# vi /etc/nginx/conf.d/hello_world.conf
+ > server {
+ > listen 4242;
+ > listen [::]:4242;
+ > server_name localhost;
+ >
+ > location / {
+ > root /var/www/hello-world;
+ > index index.html;
+ > }
+ > }
# nginx -t
Then start (or reload if already started) the nginx
service.
Now you should get the following result:
Easy.
Reverse proxy¶
Let's say that the previous "Hello world!" example is still hosted and
running on your "host server" (e.g. with IP 123.123.42.42
). And now you want to set up a reverse
proxy on your "proxy server" (through the 4242
port for example).
On your proxy server, you will have to install Nginx like described above. Then you will just have to configure it like so:
# vi /etc/nginx/nginx.conf
> ...
+ > include /etc/nginx/conf.d/hello_world_proxy.conf;
> }
# vi /etc/nginx/conf.d/hello_world_proxy.conf
+ > server {
+ > listen 80;
+ > listen [::]:80;
+ > server_name localhost;
+ >
+ > location / {
+ > proxy_pass http://123.123.42.42:4242;
+ > }
+ > }
# nginx -t
Don't forget to start (or reload if already started) the nginx
service.
If your proxy server IP address is 111.222.1.2
, then you should get the following result:
Warning
You should probably check the NAT table of the router in front of your "host server", in order
to open the 4242
port of your router and redirect it to the 4242
port of your "host
server". When doing so, in order to have a safer reverse proxy config, you might also want to
only allow traffic originating from the IP address of your reverse proxy.
In addition, you should make sure that the firewall (if any) of
your "host server" is not blocking the 4242
port.
Hosting multiple websites¶
Now lets say you want to serve multiple websites, in addition to the previous "Hello world!" site you also want a "Goodbye world!" site at the same time:
# mkdir -p /var/www/goodbye-world
# echo "Goodbye world!" > /var/www/goodbye-world/index.html
# vi /etc/nginx/nginx.conf
> ...
>
> #include /etc/nginx/conf.d/default_nginx.conf;
> include /etc/nginx/conf.d/hello_world.conf;
+ > include /etc/nginx/conf.d/goodbye_world.conf;
> }
# vi /etc/nginx/conf.d/goodbye_world.conf
+ > server {
+ > listen 4243;
+ > listen [::]:4243;
+ > server_name localhost;
+ >
+ > location / {
+ > root /var/www/goodbye-world;
+ > index index.html;
+ > }
+ > }
# nginx -t
Then start (or reload if already started) the nginx
service.
Now you should get the following result:
Password protection¶
Reference(s)
Create an .htpasswd
file containing a user name and the salted hash of its password (thanks to
the Perl crypt("your-password","your-salt")
function):
$ hash=$(perl -le 'print crypt("GPpoGPAX8H3A8N28", "ZXYv7trSx7zxvXib")')
$ echo "username:$hash" | sudo tee /path/to/.htpasswd
Now you can password protect a server, e.g.:
You can also password protect a single location, e.g.:
server {
...
location /your-location {
auth_basic "Login";
auth_basic_user_file /path/to/.htpasswd;
...
}
...
}
HTTPS digital certificate with Certbot¶
Certbot is an easy-to-use client that fetches a certificate from Let’s Encrypt - an open certificate authority launched by the EFF, Mozilla, and others - and deploys it to a web server.
Reference(s)
- https://eff-certbot.readthedocs.io/en/stable/
- https://certbot.eff.org/
- https://wiki.archlinux.org/title/Certbot
- https://wiki.gentoo.org/wiki/Let%27s_Encrypt
- https://landchad.net/certbot
- https://github.com/certbot/certbot
- https://certbot.eff.org/lets-encrypt/ubuntufocal-nginx
- https://certbot.eff.org/docs/intro.html
- https://certbot.eff.org/docs/what.html
- https://certbot.eff.org/docs/install.html
- https://certbot.eff.org/docs/using.html
Prerequisite(s)
- IPv6 must be enabled on your server!
- Install
certbot
andcertbot-nginx
:
- Get a certificate for your domain:
A wildcard certificate is a certificate that includes one or more names starting with *
.
Browsers will accept any label in place of the asterisk (*
). For example, a certificate
for *.example.com
will be valid for www.example.com
, mail.example.com
,
hello.example.com
, and goodbye.example.com
.
However, a wildcard certificate including only the name *.example.com
will not be valid
for example.com
: the substituted label can not be empty. If you want the certificate to
be valid for example.com
, you also need to include example.com
(i.e. without the *.
part) on the certificate.
Additionally, the asterisk can only be substituted by a single label and not by multiple
labels. For example, the name hello.goodbye.example.com
will not be covered by a
certificate including only the name *.example.com
. It will be covered however, by
*.goodbye.example.com
. Note that a wildcard name can not contain multiple asterisks. For
example, *.*.example.com
is not valid.
If you’d like to obtain a wildcard certificate with Certbot (i.e. from Let’s Encrypt), you will have to install one of the Certbot’s DNS plugins : https://certbot.eff.org/docs/using.html#dns-plugins
For example, here is how I obtained a wildcard certificate for *.stephane.plus
(and also
stephane.plus
), with the
certbot-dns-ovh
DNS plugin:
$ certbot plugins # verify that the Certbot plugins were installed correctly
$ sudo vi /etc/nginx/.ovh.ini # or `.linode.ini`, `cloudflare.ini` or whatever
> # OVH API credentials used by Certbot
> dns_ovh_endpoint = ovh-eu # or `ovh-na` if your VPS is in North America and not Europe
> dns_ovh_application_key = MDAwMDAwMDAwMDAw
> dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
> dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
$ sudo chmod 600 /etc/nginx/.ovh.ini
$ sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.ori_back # backup your original conf file
$ sudo certbot --installer nginx --authenticator dns-ovh --dns-ovh-credentials /etc/nginx/.ovh.ini --dns-ovh-propagation-seconds 60 -d *.stephane.plus -d stephane.plus
$ sudo certbot certificates # review certificates list
$ sudo certbot renew --dry-run --installer nginx --authenticator dns-ovh --dns-ovh-credentials /etc/nginx/.ovh.ini --dns-ovh-propagation-seconds 60
$ sudo crontab -e
> # * * * * * command_or_script_to_execute
> # - - - - -
> # | | | | |
> # | | | | +- day of week (0 - 7) (where sunday is 0 and 7)
> # | | | +--- month (1 - 12)
> # | | +----- day (1 - 31)
> # | +------- hour (0 - 23)
> # +--------- minute (0 - 59)
>
> 0 0 1 * * certbot renew --installer nginx --authenticator dns-ovh --dns-ovh-credentials /etc/nginx/.ovh.ini --dns-ovh-propagation-seconds 60
> ...
$ certbot plugins # verify that plugins were installed correctly
$ sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.ori_back # backup your original conf file
$ certbot --nginx -d example.com
$ certbot certificates # review certificates list
$ sudo certbot renew --dry-run --nginx
$ sudo crontab -e
> # * * * * * command_or_script_to_execute
> # - - - - -
> # | | | | |
> # | | | | +- day of week (0 - 7) (where sunday is 0 and 7)
> # | | | +--- month (1 - 12)
> # | | +----- day (1 - 31)
> # | +------- hour (0 - 23)
> # +--------- minute (0 - 59)
>
> 0 0 1 * * certbot renew --nginx
> ...
-
After running
certbot
, you can improve your Nginx configuration by following Mozilla recommendations: https://ssl-config.mozilla.org/#server=nginx&config=modern. -
After running
certbot
, restartnginx
. -
Now you can test your config at this addresses:
With the previous configurations, you should get at least A grades.
Tip
Certbot certificates will be saved in /etc/letsencrypt/live/
.
Certbot tips and tricks¶
-
Remove all certificates and re-generate them:
-
First, delete your generated certificates:
-
Then, remove the Certbot generated web server’s configuration leftover lines from your
nginx.conf
file (i.e. remove the "# managed by Certbot" lines) and replace them by your previous backup:$ cd /etc/nginx $ sudo diff nginx.conf nginx.conf.ori_back # review differences between original backup conf and current Certbot conf $ sudo vi nginx.conf.ori_back # optionnaly edit the original backup conf in order to back-port eventual changes (DO NOT copy any "# managed by Certbot" lines here!) $ sudo mv nginx.conf nginx.conf.certbot_back_$(date +"%Y-%m-%d_%T") # backup current Certbot conf, just in case $ sudo cp nginx.conf.ori_back nginx.conf # restore original backup $ sudo systemctl restart nginx.service $ sudo systemctl status nginx.service
-
Finally, re-generate your certificates. For example, here is how I re-generated a wildcard certificate for
*.stephane.plus
(and alsostephane.plus
), with thecertbot-dns-ovh
DNS plugin:$ certbot plugins # verify that the Certbot plugins were installed correctly $ sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.ori_back # backup your original conf file $ sudo certbot --installer nginx --authenticator dns-ovh --dns-ovh-credentials /etc/nginx/.ovh.ini --dns-ovh-propagation-seconds 60 -d *.stephane.plus -d stephane.plus $ sudo certbot certificates # review certificates list $ sudo certbot renew --dry-run --installer nginx --authenticator dns-ovh --dns-ovh-credentials /etc/nginx/.ovh.ini --dns-ovh-propagation-seconds 60
-
If this cheat sheet has been useful to you, then please consider leaving a star here.