Nginx#
Nginx is a versatile tool that can be utilized as a web server, reverse proxy, load balancer, and much more. This page provides an overview of the fundamentals of working with Nginx.
Sources:
For examples, we will run nginx in a docker container - the following cell runs nginx in a docker container.
docker run -itd --name experiment_nginx --rm -p 80:80 nginx
0f36c0c1aa1fa9936f5b0bd323c4a53a99e1f6254f5e4779af68a80e38f0e624
Now we can check if nginx is up by requesting it’s main page.
cat << EOF | displayHTML
<iframe
srcdoc='$(curl -s localhost:80)'
width='100%'
height='600px'
style='border:none;'
>
</iframe>
EOF
Note don’t forget to stop container after all.
docker stop experiment_nginx
experiment_nginx
Configuration#
Nginx is mainly controlled by its configuration files, so it’s important to understand where they are. They are usually located in the /etc/nginx/
folder. Find out more in specific page.
The following cell shows the contents of the /etc/nginx
command.
docker exec experiment_nginx ls -l /etc/nginx/
total 32
drwxr-xr-x 1 root root 4096 Sep 4 08:52 conf.d
-rw-r--r-- 1 root root 1007 May 28 13:22 fastcgi_params
-rw-r--r-- 1 root root 5349 May 28 13:22 mime.types
lrwxrwxrwx 1 root root 22 May 29 16:45 modules -> /usr/lib/nginx/modules
-rw-r--r-- 1 root root 648 May 29 16:45 nginx.conf
-rw-r--r-- 1 root root 636 May 28 13:22 scgi_params
-rw-r--r-- 1 root root 664 May 28 13:22 uwsgi_params
The most important file here is nginx.conf
- it’s the central nginx configuration file. It’s printed in the next cell:
docker exec experiment_nginx cat /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
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 /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
Changing this file will change the behavior of nginx. So in the next cell we do that. With nginx -s reload
we reload nginx - this will apply the changes to nginx. The following cell defines the most minimal configuration I’ve found so far - just returning 200 “hello” from the server.
docker exec -i experiment_nginx sh -c 'cat > /etc/nginx/nginx.conf' <<EOF
events {}
http {server{return 200 "hello";}}
EOF
docker exec -i experiment_nginx nginx -s reload
2024/12/04 16:13:55 [notice] 311#311: signal process started
Here is showen how default behaviour of the nginx changed.
curl localhost:80
hello
We just got message specified in the nginx.conf
.
Variables#
Nginx uses variables, which are denoted by the $
symbol before the variable name. There is a set of predefined variables in Nginx that you can use. Find out more in the specific page.
The following cell shows how you can display the value of an arbitrary Nginx variable; in this case, it is $localtime
.
Note: The backward slash (\
) is used here to escape the $
symbol from bash, which also uses $
to identify variables.
docker exec -i experiment_nginx sh -c 'cat > /etc/nginx/nginx.conf' <<EOF
events {}
http {
server {
listen 80;
return 200 \$time_local;
}
}
EOF
docker exec experiment_nginx nginx -s reload
2024/09/04 09:21:38 [notice] 381#381: signal process started
Let’s request gotten endpoint.
curl http://localhost:80
04/Sep/2024:09:28:54 +0000
We get something that looks like a datetime.
Location#
The location
directive allows setting configurations based on the request URI. Check corresponding section on the official site.
In the following example, nginx config defines two locations: /pattern
and /pattern/
URIs. By looking at the output, you can see exactly which location was triggered. They’re almost the same, but you’ll see that they match the different cases.
docker exec -i experiment_nginx sh -c 'cat > /etc/nginx/nginx.conf' <<EOF
events {}
http {
server {
listen 80;
location /pattern {
return 200 "location 1 - I use /pattern";
}
location /pattern/ {
return 200 "location 2 - I use /pattern/";
}
}
}
EOF
docker exec -it experiment_nginx nginx -s reload
2024/12/04 16:05:03 [notice] 106#106: signal process started
Now we can request different URIs from nginx and see what exactly has been triggered.
The following example shows what happens when you access /pattern
(no /
at the end of the command).
curl -L http://localhost:80/pattern
location 1 - I use /pattern
The next cell shows what happens when you access /pattern/wow
(we use an additional path segment).
curl -L http://localhost:80/pattern/wow
location 2 - I use /pattern/
Server#
With the server
directive, you can create so-called “virtual servers” under which you can place locations with the same configuration.
There is a strong relationship to the server_name
directive that allows nginx to understand which request should be handled by which virtual server.
Find out more in:
Server names official nginx tutorial.
How nginx processes a request page in official nginx site.
Particular page on conviguring virtual servers in nginx.
The following cell shows the basic usage - there are two servers. It’s possible to tell which server handled the request Server
header of the http response. You can specify which one you want to work with by specifying the Host
header in the request.
docker exec -i experiment_nginx sh -c 'cat > /etc/nginx/nginx.conf' <<EOF
events {}
http {
server {
server_name serv1;
location /loc1 {return 200 "Hello1";}
location /loc2 {return 200 "Hello2";}
add_header Server One;
}
server {
server_name serv2;
location /loc1 {return 200 "Hello1";}
location /loc2 {return 200 "Hello2";}
add_header Server Two;
}
}
EOF
docker exec experiment_nginx nginx -s reload
2024/12/26 10:14:25 [notice] 110#110: signal process started
The following cell shows requests with an unspecified Host
header - by default it will go to the server higher in the config.
curl -I localhost/loc1
curl -I localhost/loc2
HTTP/1.1 200 OK
Server: nginx/1.27.3
Date: Thu, 26 Dec 2024 10:14:29 GMT
Content-Type: text/plain
Content-Length: 6
Connection: keep-alive
Server: One
HTTP/1.1 200 OK
Server: nginx/1.27.3
Date: Thu, 26 Dec 2024 10:14:29 GMT
Content-Type: text/plain
Content-Length: 6
Connection: keep-alive
Server: One
As a result, there is a One
value under the Server
key of the responses.
The same experiment but with Host: serv2
which is corresponds to the value server_name serv2
.
curl -I localhost/loc1 -H "Host: serv2"
curl -I localhost/loc2 -H "Host: serv2"
HTTP/1.1 200 OK
Server: nginx/1.27.3
Date: Thu, 26 Dec 2024 11:30:26 GMT
Content-Type: text/plain
Content-Length: 6
Connection: keep-alive
Server: Two
HTTP/1.1 200 OK
Server: nginx/1.27.3
Date: Thu, 26 Dec 2024 11:30:26 GMT
Content-Type: text/plain
Content-Length: 6
Connection: keep-alive
Server: Two
As a result, the headers are the same as specified as the second virtual server.
Reverse proxy#
Another way to use nginx is as a reverse proxy, so you can redirect requests. You can define it with the proxy_pass
directive in the server
context.
Findout more in specific page.
The following config file makes requests to nginx to redirect to google.com
.
docker exec -i experiment_nginx sh -c 'cat > /etc/nginx/nginx.conf' <<EOF
events {}
http {
server {
listen 80;
location / {proxy_pass "https://google.com/";}
}
}
EOF
docker exec experiment_nginx nginx -s reload
2024/09/04 08:55:31 [notice] 146#146: signal process started
Now, when we access localhost:80
, we get a message that we’ve been redirected.
curl -s localhost:80 | displayHTML
By adding the -L
option to the curl
command, we can follow redirects, allowing us to retrieve Google’s main page.
Logs#
Logging in Nginx is very useful for troubleshooting. There are two main log files in Nginx:
/var/log/nginx/access.log
: Logs all incoming requests and their details./var/log/nginx/error.log
: Logs cases that lead to errors.
Find out more about logging setup in nginx:
Corresponding page on this site.
error_log
direcitve description.ngx_http_log_module
module that implements featuers associated with configuring logging in nginx.
For reasons that are still unclear, we need to reorder the log files in order to be able to read them. So in the following cell we do this for our container.
docker exec -i experiment_nginx bash << EOF
rm /var/log/nginx/access.log /var/log/nginx/error.log
touch /var/log/nginx/access.log /var/log/nginx/error.log
nginx -s reload
EOF
And here is a request to nginx to print the contents of access.log
.
curl localhost:80 &> /dev/null
docker exec experiment_nginx cat /var/log/nginx/access.log
172.17.0.1 - - [22/Jul/2024:07:54:09 +0000] "GET / HTTP/1.1" 301 220 "-" "curl/7.81.0"
Connections management#
This section cosiders capabilities of the nginx to manage connections. Check the specific page for this topic.
Build nginx#
To enable some features of the nginx you’ll need to build nginx from sources. For example, to include module for example ngx_http_stub_status_module
you need to build nginx with special parameter --with-http_stub_status_module
only after that operation you can use features of the included module.
Check corresponding section of the nginx installation page.
The following cell represents dockerfile that can be used for building image of the custom nginx.
cat nginx_files/build_nginx_docker
FROM debian:bookworm-slim
RUN apt update && \
apt install --no-install-recommends --no-install-suggests -y \
gnupg1 \
ca-certificates \
curl \
devscripts \
equivs \
git \
libxml2-utils \
lsb-release \
xsltproc \
libpcre3 \
libpcre3-dev \
zlib1g \
zlib1g-dev
ARG NGINX_VERSION="1.27.3"
ARG CONFIG_ARGUMENTS="--conf-path=/etc/nginx/nginx.conf --with-http_stub_status_module"
RUN curl -f -L -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
tar -zxvf nginx-${NGINX_VERSION}.tar.gz && \
cd nginx-${NGINX_VERSION} && \
./configure ${CONFIG_ARGUMENTS} && \
make && make install
ENV PATH=$PATH:/usr/local/nginx/sbin
CMD ["nginx", "-g", "daemon off;"]
There is such crucial features:
Installing packages that are required for building nginx:
apt update ... && apt install ...
.Downloading nginx compiling and instiling it:
curl -f -L -O http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \ tar -zxvf nginx-${NGINX_VERSION}.tar.gz && \ cd nginx-${NGINX_VERSION} && \ ./configure --conf-path=/etc/nginx/nginx.conf && \ make && make install
The
./configure
command is particularly important as it allows you to specify additional arguments that affect the behavior of the resulting build. You can check the list of parameters and their meanings on the corresponding page. For convenience, in a Dockerfile, there is a special argumentCONFIG_ARGUMENTS
where you can specify the set of arguments you want to use to build Nginx.
Add the path of the installation to the
PATH
environment variable - so you can access nginx just by using thenginx
command.
In some specific sections of this description, a custom_nginx
image can be used, which can be created with the following code:
docker build -f nginx_files/build_nginx_docker -t custom_nginx . &> /dev/null
With the nginx -V
command, you can print the Nginx version, compiler version, and configure parameters. Use this to verify if you are indeed working with a custom Nginx build.
docker run -it --rm --name experiment_nginx custom_nginx nginx -V
nginx version: nginx/1.27.3
built by gcc 12.2.0 (Debian 12.2.0-14)
configure arguments: --conf-path=/etc/nginx/nginx.conf --with-http_stub_status_module