Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Practical Nginx Advanced Configuration: Variables, Log Management, SSL, Compression and URL Rewrite

Tech 2

Nginx Server Status Page

The status monitoring feature relies on the ngx_http_stub_status_module which is not compiled by default. You need to add the --with-http_stub_status_module parameter during Nginx compilation to enable it. Note that the status page reflects global server metrics, not per-virtual-host data.

location /server_status {
    stub_status;
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/conf/.status_passwd;
    allow 10.0.0.0/8;
    allow 127.0.0.1;
    deny all;
}

To generate the auth file, use htpasswd -c /etc/nginx/conf/.status_passwd admin and set a password for the admin user.

Third-Party Echo Module

The openresty echo-nginx-module adds dynamic output capabilities to Nginx configs. You can download the source from its official GitHub repository.

Step 1: Update Virtual Host Config

server {
    listen 80;
    server_name demo.mydomain.io;
    root /srv/webroot;

    location /client-ip {
        echo "Hello, your public IP address is:";
        echo $remote_addr;
    }
}

Step 2: Recompile Nginx with Echo Module

Extract the module source to /opt/echo-nginx-module, then recompile Nginx with the existing build flags plus the new module:

cd /opt/nginx-1.24.0
systemctl stop nginx
# Get existing compile flags
nginx -V
# Add --add-module=/opt/echo-nginx-module to the configure command
./configure --prefix=/etc/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --add-module=/opt/echo-nginx-module
make && make install

Step 3: Verify Config and Restart

nginx -t
systemctl start nginx

You can visit http://demo.mydomain.io/client-ip to confirm the module works correctly.

Nginx Variables

All official Nginx variables are documented at http://nginx.org/en/docs/varindex.html.

Common Built-in Variables

Variable Name Description
$remote_addr Public IP address of the requesting client
$proxy_add_x_forwarded_for Appends the client IP to the X-Forwarded-For header, multiple values are separated by commas. Used for IP passthrough in reverse proxy chains
$args Full query string from the request URL
$arg_<param_name> Value of a specific query parameter, e.g. $arg_id returns the value of the id query parameter
$document_root Root directory path for the current virtual host
$uri Request URI without query parameters
$host Host header value from the request
$remote_port Ephemeral port used by the client to make the request
$request_method HTTP request method (GET, POST, PUT, DELETE etc.)
$request_filename Absolute disk path of the requested resource
$request_uri Full request URI including query parameters, without the host name
$scheme Request protocol (http, https, ftp etc.)
$server_addr IP address of the Nginx server handling the request
$server_name Server name of the matched virtual host
$server_port Port the request was received on
$http_user_agent Full user agent string of the client browser
$http_cookie Full cookie string sent by the client
$sent_http_<header_name> Value of a specified response header, with dashes replaced by underscores

Note: For reverse proxy deployments, $proxy_add_x_forwarded_for will accumulate IPs across all proxy layers in the request chain, letting the origin server see the original cliant IP even when multiple proxies are in use.

Example config to test variables:

location /var-demo {
    default_type text/plain;
    echo "Client IP: $remote_addr";
    echo "Query String: $args";
    echo "User ID Param: $arg_user_id";
    echo "Site Root: $document_root";
    echo "Request URI: $uri";
    echo "Host: $host";
    echo "User Agent: $http_user_agent";
    echo "Full Request URL: $scheme://$host$request_uri";
}

Custom Variables

You can define custom variables using the set directive, which is valid in server, location and if blocks:

location /custom-var {
    default_type text/plain;
    set $site_id "demo_site_001";
    set $listen_port $server_port;
    echo "Site ID: $site_id";
    echo "Listening Port: $listen_port";
}

Custom Favicon

The favicon.ico file is the icon displayed in browser tabs and boookmark lists. Browsers automatically request this file when loading a site, and missing favicons will generate 404 errors in your logs. To set a custom favicon:

  1. Download the desired favicon (e.g. from any public site, or create your own)
  2. Place it in the root directory of your virtual host:
wget https://example.com/favicon.ico -P /srv/webroot/

Clear your browser cache and reload the site to see the new icon.

Custom Access Logs

Nginx allows you to define custom log formats for different virtual hosts, separate from the global error log.

Custom Text Log Format

Define log formats in the global http block, before any include directives for virtual host configs to avoid errors:

log_format main_log '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    '$server_name:$server_port';

# Apply to virtual host
server {
    listen 80;
    server_name demo.mydomain.io;
    access_log /var/log/nginx/demo.access.log main_log;
}

JSON Format Logs

JSON formatted logs are easier for log collection tools like ELK Stack to parse:

log_format json_log '{"@timestamp":"$time_iso8601",'
                    '"server_ip":"$server_addr",'
                    '"client_ip":"$remote_addr",'
                    '"bytes_sent":$body_bytes_sent,'
                    '"req_duration":$request_time,'
                    '"upstream_resp_time":"$upstream_response_time",'
                    '"upstream_host":"$upstream_addr",'
                    '"host":"$host",'
                    '"request_uri":"$uri",'
                    '"x_forwarded_for":"$http_x_forwarded_for",'
                    '"referer":"$http_referer",'
                    '"user_agent":"$http_user_agent",'
                    '"status_code":"$status"}';

# Apply to virtual host
access_log /var/log/nginx/demo.json.log json_log;

Log Rotation

Unmanaged Nginx logs can grow very large over time, leading to disk space issues. Use a scheduled script to rotate logs automatically:

#!/bin/bash
# Path: /opt/scripts/nginx_log_rotate.sh
LOG_DEST="/var/log/nginx/archive"
NGINX_LOG_DIR="/etc/nginx/logs"
NGINX_PID_FILE="/var/run/nginx.pid"
YESTERDAY=$(date -d "-1 day" +%Y%m%d)

# Create archive directory if it doesn't exist
[ -d "$LOG_DEST" ] || mkdir -p "$LOG_DEST"

# Rotate access and error logs
mv "$NGINX_LOG_DIR/access.log" "$LOG_DEST/access_$YESTERDAY.log"
mv "$NGINX_LOG_DIR/error.log" "$LOG_DEST/error_$YESTERDAY.log"

# Signal Nginx to create new log files
kill -USR1 $(cat "$NGINX_PID_FILE")

# Delete logs older than 30 days
find "$LOG_DEST" -type f -mtime +30 -delete

Make the script executable: chmod +x /opt/scripts/nginx_log_rotate.sh Add a cron job to run daily at 2 AM:

0 2 * * * /opt/scripts/nginx_log_rotate.sh

Gzip Compression

Nginx supports compressing response content before sending it to clients, reducing bandwidth usage and improving load times. This feature relies on the ngx_http_gzip_module. Common configuration parameters:

http {
    gzip on;
    gzip_comp_level 6; # Compression level 1-9, 6 balances speed and compression ratio
    gzip_min_length 2k; # Only compress files larger than 2KB
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary on; # Add Vary: Accept-Encoding header to responses
    gzip_static on; # Serve pre-compressed .gz files if they exist, avoids on-the-fly compression overhead
}

Note: Gzip does not compress image files, as they are already compressed in most cases. The curl command line tool does not accept compressed responses by default, so use browser dev tools to verify compression is working.

HTTPS Configuration

HTTPS encrypts data transmitted between clients and servers, preventing sensitive data from being intercepted. Nginx HTTPS support relies on the ngx_http_ssl_module, which is enabled by default in most packaged Nginx installations.

Common SSL Configuration Parameters

server {
    listen 443 ssl;
    server_name app.mydomain.com;
    root /srv/webroot/app;

    ssl_certificate /etc/nginx/ssl/app.mydomain.com_fullchain.crt;
    ssl_certificate_key /etc/nginx/ssl/app.mydomain.com_privkey.key;
    ssl_protocols TLSv1.2 TLSv1.3; # Disable old insecure SSL protocols
    ssl_session_cache shared:ssl_cache:20m; # Shared SSL session cache across worker processes
    ssl_session_timeout 10m;
}

For self-signed certificates, you can generate them using OpenSSL. For public facing sites, use a trusted CA like Let's Encrypt to issue certificates to avoid browser security warnings.

URL Rewrite Functionality

The ngx_http_rewrite_module provides URL rewriting capabilities, which are useful for redirecting old URLs to new paths, enforcing HTTPS, and improving site security. It requires the PCRE library for regular expression support.

If Directive

The if directive is used for conditional logic in server and location blocks. It only supports single condition checks, no else/elif chains:

location /protocol-check {
    default_type text/plain;
    if ($scheme = http) {
        echo "Request uses unencrypted HTTP protocol";
    }
    if ($scheme = https) {
        echo "Request uses encrypted HTTPS protocol";
    }
}

Supported conditional operators:

  • =/!=: Exact string match
  • ~/!~: Case-sensitive regular expression match/non-match
  • ~*/!~*: Case-insensitive regular expression match/non-match
  • -f/!-f: Check if requested file exists/does not exist
  • -d/!-d: Check if requested directory exists/does not exist
  • -e/!-e: Check if requested file/directory/symlink exists/does not exist

Return Directive

The return directive immediately sends a response to the client, skipping all subsequent config directives. It can be used in server, location and if blocks:

# Temporary redirect (302, no client cache)
location /old-path {
    return 302 https://demo.mydomain.io/new-path;
}

# Permanent redirect (301, cached by browsers long-term)
location /legacy-page {
    return 301 https://demo.mydomain.io/current-page;
}

# Return custom 403 response
location /restricted {
    return 403 "Access to this resource is forbidden";
}

Key difference between 301 and 302 redirects:

  • 301 (Moved Permanently): Browsers cache the redirect indefinitely, so even if the Nginx config is removed, clients will continue to redirect until their cache is cleared.
  • 302 (Found): No client caching, every request hits the server to get the redirect target, so changes to the redirect take effect immediately.
Tags: nginx

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.