What is NGINX?
NGINX is a web server that is often used to serve static content, as a reverse proxy, and as a load balancer. It is known for its high performance, stability, and low resource consumption, making it a popular choice for many websites and web applications.
NGINX listens for incoming requests on the configured port and address, and then processes the requests according to the specified configuration. This typically involves serving static content from the configured root directory, forwarding requests to a backend server or application, or applying various transformations or filters to the request or response.
Some of the benefits of using NGINX include its high performance, ability to handle a large number of concurrent connections, and its flexibility and extensibility through the use of modules and custom configuration. NGINX supports a wide range of protocols including HTTP, HTTPS, TCP UDP, SMTP, POP3, and IMAP.
Enabling Envoy Proxy with Solo Gloo Gateway
While NGINX is a popular proxy technology, Envoy Proxy has proven to be a more scalable and more modular technology for cloud native application use-cases.
Envoy Proxy is a powerful, extensible, proxy built on C++ and is a graduated project in the Cloud Native Computing Foundation (CNCF). Envoy is not owned by any one vendor and is a big reason why we’ve seen an explosion in projects using it to power Layer 7 including projects like API gateways, service meshes, and even CNIs.
Solo Gloo Gateway leverages Envoy Proxy as the core engine to enable a wide range of extended capabilities beyond NGNIX.
NGINX configuration examples: Understanding directives
In NGINX, directives are instructions that are used to configure the web server. They are typically specified in the nginx.conf
configuration file, located in the /etc/nginx/
directory, or in additional configuration files that are included from the main configuration file.
Directives are typically organized into blocks, which are groups of directives that are related to a specific context or function. For example, a server block is used to specify settings for a specific server, and a location block is used to specify settings for a specific URL path.
Here is an example nginx.conf
file that includes some common directives and blocks:
# the number of worker processes to use worker_processes 4; # include additional configuration files include /etc/nginx/conf.d/*.conf; http { # HTTP server settings server { # listen on port 80 listen 80; # server name server_name example.com; # default location location / { # root directory root /var/www/html; # index file index index.html; } } }
In this example, the worker_processes
directive specifies the number of worker processes that NGINX should use, the include
directive is used to include additional configuration files, and the server
block contains settings for a specific server. Within the server block, the location
block specifies settings for the default location, which is the root URL of the server.
There are additional directives – see the official documentation for more detail.
Below we’ll describe the most important blocks in NGINX configuration.
HTTP block
An HTTP block in an NGINX configuration file specifies global configuration settings for the NGINX HTTP server. This can include settings such as the number of worker processes to run, the maximum number of connections allowed, and the location of log files.
Here is an example of an HTTP block in an NGINX configuration file:
http { worker_processes 4; worker_rlimit_nofile 8192; client_max_body_size 128m; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; server { ... } }
In this example, the HTTP block includes settings for the number of worker processes, the maximum number of file descriptors, and the maximum allowed body size for client requests. It also specifies the location of the access and error logs. The HTTP block also contains a server block that specifies the configuration for a web server.
Server blocks
In NGINX, a server block is a configuration block that specifies how the server should handle traffic for a specific domain or group of domains. This allows you to define different settings and behavior for different websites or applications that are hosted on the same server.
Here is an example of a server block in an NGINX configuration file:
server { listen 80; server_name www.example.com; location / { root /var/www/html/example; index index.html index.htm; } location /app { proxy_pass http://localhost:3000; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
In this example, the listen
parameter specifies that the server should listen for incoming connections on port 80. The server_name
parameter specifies the domain name that this server block should handle.
The listen
and server_name
parameters are used together to define which domains and ports the server should listen for and handle traffic for. In this example, the server will only handle unencrypted traffic for the www.example.com domain on port 80.
Location blocks
In NGINX, a location block is a configuration block that specifies how the server should handle requests for specific URLs or URL patterns. This allows you to define different behavior for different parts of your website or application, such as serving static files from a specific directory or proxying requests to another server.
Here is an example of a location block in an NGINX configuration file:
server { ... location / { root /var/www/html/example; index index.html index.htm; } location /app { proxy_pass http://localhost:3000; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
In this example:
- The location block for / specifies that requests for the root URL of the website should be handled by serving files from the
/var/www/html/example
directory. - The location block for
/app
specifies that requests for the/app
URL should be proxied to thelocalhost
on port 3000. - The location block for
/50x.html
specifies how the server should handle server errors by serving a specific file.
NGINX configuration of reverse proxy
An NGINX reverse proxy is a type of configuration in which NGINX acts as a proxy server that listens for incoming connections and forwards them to one or more upstream servers. This allows NGINX to handle incoming requests and forward them to the appropriate backend server.
To configure an NGINX reverse proxy, you can use the proxy_pass
directive in a location block in your NGINX configuration. Here is an example of how to configure an NGINX reverse proxy:
server { listen 80; server_name www.example.com; location / { proxy_pass http://localhost:3000; } }
In this example, the location block for / specifies that all requests to the www.example.com domain should be proxied to the localhost on port 3000. This means that NGINX will receive incoming requests and forward them to the server running on localhost on port 3000.
You can also use the proxy_set_header
directive to specify additional headers to be added to the proxied request. For example:
server { listen 80; server_name www.example.com; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://localhost:3000; } }
In this example, the proxy_set_header
directives specify that the Host
and X-Real-IP
headers should be added to the proxied request, with their values set to the hostname of the incoming request and the IP address of the client making the request, respectively. This can be useful for passing information about the incoming request to the upstream server.
4 common NGINX configuration mistakes to avoid
The error_log off
directive
A common mistake is to assume that the error_log off
directive will disable logging. However, the error_log
directive is unlike access_log
and doesn’t accept the “off” parameter. If the configuration includes error_log off
, NGINX will create an error log file with the name “off,” which will be visible in the default NGINX config file directory (i.e., /etc/nginx
).
In any case, it is not recommended to disable the error log because it provides vital information for debugging NGINX problems. However, when storage is very limited, logging too much data may take up all the available disk space. In such cases, disabling the error log might make sense, but the directive should be included within the main context of the configuration:
error_log /dev/null emerg;
This directive only applies after NGINX has read and validated the configuration. Thus, whenever NGINX starts up (or you reload the configuration), it will log to the default location for an error log (i.e., /var/log/nginx/error.log
) until it has validated the configuration. It is possible to change the log directory by including -e <error_log_location>
in the nginx
command.
Note: You can use run nginx -t
command to verify your nginx configuration file.
The proxy_buffering off
directive
Setting the proxy_buffering
directive to “off” is a common mistake because it can cause performance issues and unexpected behavior in NGINX. When proxy buffering is disabled, NGINX receives a response from the proxied server and immediately sends it to the client without storing it in a buffer. This can cause problems if the response is large or if the connection between NGINX and the client is slow, because it can result in increased memory usage and potentially lead to request timeouts.
To avoid this mistake, it is recommended to always enable proxy buffering in NGINX by setting the proxy_buffering
directive to “on”. This will cause NGINX to store the response from the proxied server in a buffer before sending it to the client, which can improve performance and stability. Here is how to do it:
proxy_buffering on;
This will enable proxy buffering for all proxied requests handled by NGINX. You can also adjust the size of the buffer and other settings related to proxy buffering by using additional parameters with the proxy_buffering
directive. For more information, you can refer to the NGINX documentation.
Improper use of the if
directive
The if directive can be difficult to use, especially within a location{}
block. This directive doesn’t always do what is expected, and it can sometimes result in segmentation faults. Usually, rewrite
and return
are the only directives that are always safe to us in if{}
blocks.
Here is an example that uses the if
directive to identify requests that include the X-Test header. NGINX will return a 430 error, indicating that the request header fields are too large, and intercept it at the @error_430
location. Then it will proxy the request to an upstream group named “y”:
location / { error_page 430 = @error_430; if ($http_x_test) { return 430; } proxy_pass http://a; } location @error_430 { proxy_pass y; }
In many cases, it is possible to avoid the if
directive entirely. For example, if the HTTP request includes an X-Test header, the map{}
block will set the $upstream_name
variable to y
and the NGINX will proxy the to an upstream group of the same name:
map $http_x_test $upstream_name { default "y"; "" "b"; } # ... location / { proxy_pass http://$upstream_name; }
Excessive health checks
Excessive health checks can be a problem in NGINX configuration if the server is configured to perform too many checks on the health of its upstream servers. Health checks are used to determine the status of upstream servers, and NGINX can be configured to perform these checks at regular intervals or on each request.
If the NGINX server is configured to perform too many health checks, it can place a heavy load on the upstream servers and impact their performance. This can lead to delays in processing requests and overall reduced performance of the server.
To avoid this issue, it is important to carefully consider the frequency and type of health checks that are performed by the NGINX server. You can use the check_interval
and check_http_send
directives to specify the interval between checks and the HTTP request that is used to perform the check, respectively. You can also use the check_type
directive to specify the type of check that is performed, such as a TCP or HTTP check.
It is generally recommended to use health checks sparingly and to set the interval between checks to a reasonable value that does not impact the performance of the upstream servers. You can also consider using other methods to monitor the health of the upstream servers, such as using a monitoring tool or setting up alerts to notify you of any issues.