nginx is a very familiar tool for the server (backend) in general, it is very necessary to understand the basic concepts of it. In this article, I will share with you my personal notes on the process of learning about nginx.
The two main sources I refer to and learn from here are:
What is nginx?
nginx is the server that provides content for the website, not only that, nginx can also be used as a reverse proxy
, HTTP cache
as shown below.
Not only that, in case the system has many servers, nginx also acts as a load balancer
to help coordinate incoming requests to the respective servers.
As the illustration above you can see nginx can act as a Reverse proxy
In case the system needs encryption, instead of having to encrypt and decrypt at each server, you will set up encryption and decryption at nginx server.
You can refer to the nginx installation at the link https://www.nginx.com/resources/wiki/start/topics/tutorials/install/
Configuration Terms
There are two basic config concepts in nginx:
- Context
- Directive
1 2 3 4 5 6 7 |
<span class="token directive"><span class="token keyword">http</span></span> <span class="token punctuation">{</span> <span class="token comment"># context, tương đương như scope</span> <span class="token directive"><span class="token keyword">server</span></span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">listen</span> <span class="token number">8080</span></span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">root</span> path_to_mysite/</span> <span class="token punctuation">;</span> <span class="token comment"># directive: giống như một config</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Since context
is equivalent to scope, like the code above contexts
can be nested.
There are 3 contexts
of nginx to pay attention to:
- http
- server
- location
The configs will usually be placed in the nginx config file with the name nginx.conf
, by default, the system’s nginx service
will use the /usr/local/etc/nginx/nginx.conf
file (this is the path on the system). mine with macOS operating system, you with Linux or Windows operating systems will have a difference).
However you can still use your own nginx config file by using the command below
1 2 |
nginx -c file_path |
Reload nginx
1 2 |
nginx -s reload |
The basic difference between nginx restart
and nginx reload
is that:
nginx reload
will kill the old process of nginx, based on the config file to restart a new process (note here nginx service is NOT OFF ), in case there is a problem with the config file, the whole process will be killed stopnginx restart
will turn the nginx OFF and ON again the new nginx service. However, if there is a problem with the config file, the nginx service will be STOPED
Serve static content
With static content like HTML or CSS, you need to add the definition of types as follows:
1 2 3 4 5 6 7 |
<span class="token directive"><span class="token keyword">http</span></span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">types</span></span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">text/html</span> html</span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">text/css</span> css</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
These types can be referenced in the file mime.types
on your system (for me it’s the file /usr/local/etc/nginx/mime.types
)
For example with css
, if we don’t have the text/css css;
then when loading the .css
file, the browser will understand it as a text/plain
file as shown below, now the styles inside the css
file will not be applied to your HTML page.
If there is a directive text/css css;
then the browser will interpret it as a text/css
file, then the styles will be applied to your HTML page.
nginx location block
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> URI</span> <span class="token punctuation">{</span> <span class="token comment"># handle response</span> <span class="token punctuation">}</span> |
There are 3 ways to configure the URI:
- Regex match
- Prefix match
- Exact match
With Regex match
you can do like the following example:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> ~ /count/[0-9]</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">root</span> path</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
~
symbol shows that you will use regex here, but by default this is case sensitive
, and if you add *
symbol, it will be case insensitive
like the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="token comment"># Regex match - case sensitive</span> <span class="token directive"><span class="token keyword">location</span> ~ /test[0-9]</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Hello from test"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment"># Lúc này /test0 sẽ khác với /Test0</span> <span class="token comment"># Regex match - case insensitive</span> <span class="token directive"><span class="token keyword">location</span> ~* /test/[0-9]</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Hello from haha test"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment"># Lúc này /test0 sẽ giống như /Test0</span> |
With Prefix match
if you set URI = test
then other URIs eg:
- /testing
- /tested
will also match.
As for Exact match
, you add the =
sign in front of the URI as follows:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> = URI</span> <span class="token punctuation">{</span> <span class="token comment"># response</span> <span class="token punctuation">}</span> |
VD:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> = /test</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Hello from test"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
At this time, config only matches URI = “/test”, the URIs like “/testing”, “/tested”, … will not be matched.
In terms of priority, Regex match
has higher precedence than Prefix match
. However, if you use ^~
– Preferential match
, the Prefix match
will now have a higher priority than the Regex match
1 2 3 4 5 |
<span class="token comment"># Preferential match</span> <span class="token directive"><span class="token keyword">location</span> ^~ /test1</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Hello from test"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
With response you have 3 ways to write as follows:
Method 1 (using return):
1 2 3 4 5 |
<span class="token directive"><span class="token keyword">location</span> /test</span> <span class="token punctuation">{</span> <span class="token comment"># return HTTP_STATUS_CODE "response content"</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Response content"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
In the above writing, in case you set HTTP_STATUS_CODE = 200
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> /test</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Response content"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
You will get the following response:
what about with setting HTTP_STATUS_CODE = 404
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> /not-found</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">404</span> <span class="token string">"Response not found"</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
You will get the following response:
Method 2 (using root):
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> /URI</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">root</span> path</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
With writing using root
, the URI will be automatically appended to the path then you will have path/URI
VD:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> /test</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">root</span> /var/www/home</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
When the client accesses the /test
URI, the request will be redirected to the directory /var/www/home
+ /test
= /var/www/home/test
This way of writing will “tell” nginx to look for the index.html
file (default). However, what if there is no index.html
file in /var/www/home/test
folder?
Now use try_files
as follows:
1 2 3 4 5 |
<span class="token directive"><span class="token keyword">location</span> /vegetables</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">root</span> path_to_mysite/</span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">try_files</span> /vegetables/veggies.html /index.html =404</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Writing the above means that when the request comes to /vegetables
, look for the /vegetables/veggies.html
file in the path_to_mysite/vegetables
folder, if you don’t see it, then look for the index.html
file in path_to_mysite/
, and if that’s the case , you can’t see the index.html
. index.html
will return 404 NOT FOUND
error
Method 3 (using alias):
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> URI</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">alias</span> path</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
This way of writing uses the alias
keyword, alias DOES NOT APPEND the URI to the path for you as root
instead you have to specify the path explicitly. For example:
1 2 3 4 5 6 7 8 |
<span class="token directive"><span class="token keyword">location</span> /fruits</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">root</span> path_to_mysite/</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token directive"><span class="token keyword">location</span> /carbs</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">alias</span> path_to_mysite/fruits</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
With the above configuration, when accessing 2 URIs, /fruits
and /carbs
, the request will be moved to the path_to_mysite/fruits
folder
Redirect & Rewrite
Redirect:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> URI1</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">307</span> REDIRECT_URI</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Here 307
is the HTTP Code of the redirect request
Rewrite:
In case you do not want to be redirected to another page, keep the same URL but only the content is different, rewrite
is a solution.
VD:
1 2 |
<span class="token directive"><span class="token keyword">rewrite</span> ^/number/(w+) /count/ <span class="token variable">$1</span></span> <span class="token punctuation">;</span> |
In the above example you see (w+)
surrounded by a pair of parentheses ()
, which makes it possible to use its value at $1
. For example if the URI is /number/100
then the value of $1
will be 100
Note that rewrite has higher priority than location
Variables
In nginx there are 2 main types of variables:
- Configuration variables (these are variables that you will define yourself):
set $var 'something';
- NGINX Module variables (these are available variables, defined by nginx module):
$http
,$uri
,$args
– query string params
1 2 3 4 5 6 7 8 9 10 |
<span class="token directive"><span class="token keyword">set</span> <span class="token variable">$isthursday</span> <span class="token string">'No'</span></span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">if</span> ( <span class="token variable">$date_local</span> ~ <span class="token string">'Thursday'</span> )</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">set</span> <span class="token variable">$isthursday</span> <span class="token string">'Yes'</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token directive"><span class="token keyword">location</span> /inspect</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">return</span> <span class="token number">200</span> <span class="token string">"Is Thursday: <span class="token variable">$isthursday</span> "</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
In the above example, we check nginx’s predefined variable $date_local
– the current date to see if it is Thursday (Thursday) or not? If true, assign the $isthursday
variable the value Yes
(this variable is initially set to the default value of No
)
In nginx, you proceed to “embed” the value of a variable into a string by adding a $
character in front of the variable name as in the above example with the isthursday
variable.
Logging
nginx has an access log
and an error log
that stores the log of incoming connection requests and the error log, respectively
1 2 |
nginx -V |
Run the above command and you can see the path to the default nginx access.log
and error.log
files on the system.
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> URI</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">access_log</span> log_file_path</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
By setting as above, you can specify the file containing the access log separately from the default nginx access.log file.
Reverse proxy
The concept of Reverse proxy
you can refer to https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/
Simply put, the Reverse proxy
will be placed in front of the server, the client instead of interacting directly with the server, it will interact with the reverse proxy
. Every request from the client or the response from the server must go through a reverse proxy
You can set up a simple reverse proxy like this:
1 2 3 4 |
<span class="token directive"><span class="token keyword">location</span> /express</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">proxy_pass</span> <span class="token string">'http://localhost:7777/'</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
This setting means that when sending a request to /express
, the request will be forwarded to the address http://localhost:7777/
In the path of proxy_pass
, if we leave /
behind localhost:7777
, nginx will automatically redirect to localhost:7777/express
With headers, we have 2 basic directives as follows add_header
, proxy_set_header
1 2 3 4 5 6 |
<span class="token directive"><span class="token keyword">location</span> /express</span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">add_header</span> proxied nginx</span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">proxy_set_header</span> custom myown</span> <span class="token punctuation">;</span> <span class="token directive"><span class="token keyword">proxy_pass</span> <span class="token string">'http://localhost:7777/'</span></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
add_header
will add the header of the response returned from the proxy
proxy_set_header
will add to the header of the request sent to the server from the proxy, so we cannot see this header in the browser. The reason is because in the browser you are only INTERACTING DIRECTLY WITH PROXY , not with the server.
The figure below depicts the “place” of adding the add_header
and proxy_set_header
If you want to see it, you can directly log the header in the server to your console as follows:
1 2 3 4 5 6 7 |
app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"req.headers"</span> <span class="token punctuation">,</span> req <span class="token punctuation">.</span> headers <span class="token punctuation">)</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"req.url"</span> <span class="token punctuation">,</span> req <span class="token punctuation">.</span> url <span class="token punctuation">)</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">"req.route"</span> <span class="token punctuation">,</span> req <span class="token punctuation">.</span> route <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token string">'I am an endpoint'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Above I use expressJS
to make the illustration server, you can use any server framework you like.
And this is the result when logging to the console
Reference code
- Part location, redirect, rewrite, variable: https://github.com/tuananhhedspibk/BlogCode/tree/main/NginxCrash/basic
- The reverse-proxy section: https://github.com/tuananhhedspibk/BlogCode/tree/main/NginxCrash/reverse-proxy
Conclude
The above are some of my notes during my self-study about nginx. Thank you for patiently reading all, see you in part 2 (to be continued).