NGINX configuration

Directives, examples, and 4 mistakes to avoid

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.

Learn more about Gloo Gateway.

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 the localhost 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.

Cloud connectivity done right