Skip to main content

NGINX / Server Setup

NGINX error_log_write Module: Conditional Logging

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The NGINX error_log_write module brings conditional error logging to NGINX. This powerful module lets administrators write custom log entries based on request attributes. Unlike NGINX’s native error_log directive, the NGINX error_log_write module creates targeted debugging entries from your configuration file.

Why You Need Conditional Error Logging

NGINX’s native error_log directive is powerful but limited. It specifies where errors go and at what level. However, it cannot:

  • Write custom messages for specific conditions
  • Log only when certain variables match
  • Create debugging entries without code changes
  • Track specific request patterns across locations

The NGINX error_log_write module solves these limitations. It inserts custom log entries at any severity level. Entries are triggered by NGINX variables and conditions.

How the Module Works

This module operates during NGINX’s log phase. This phase runs after request processing completes. This timing is significant:

  1. All request variables are fully populated
  2. The response has already been sent
  3. Upstream processing is complete
  4. There’s no performance impact on response time

When NGINX processes a request, each directive is evaluated. If the condition is true, the message goes to the error log.

Configuration Inheritance

Log entries in http context propagate to all servers. Server entries propagate to all locations. This inheritance is unconditional. Child configurations receive parent entries plus local ones.

Installing the NGINX error_log_write Module

RHEL, CentOS, AlmaLinux, Rocky Linux

First, enable the GetPageSpeed repository:

sudo dnf install https://extras.getpagespeed.com/release-latest.rpm

Then install the module:

sudo dnf install nginx-module-error-log-write

After installation, load the module in /etc/nginx/nginx.conf:

load_module modules/ngx_http_error_log_write_module.so;

Verify the installation:

nginx -t

Note: This module is available for RHEL-based distributions only. Debian and Ubuntu packages are not yet available.

The error_log_write Directive

The module provides a single, flexible directive:

Syntax: error_log_write [level=log_level] message=text [if=condition] [if!=condition];

Default: No default (must be explicitly configured)

Context: http, server, location

Parameters

Parameter Description Required
level= Log level for the entry No (default: error)
message= The log message (supports variables) Yes
if= Condition that must be true No
if!= Condition that must be false No

Available Log Levels

The directive supports these log levels:

Level Description
emerg System is unusable
alert Immediate action required
crit Critical conditions
error or err Error conditions (default)
warn Warning conditions
info Informational messages
debug Debug messages (requires debug build)
stderr Direct stderr output

Important: The notice level is not functional in the current release.

Basic Configuration Examples

Simple Logging

Log a message for every request to a location:

location /api {
    error_log_write "message=API endpoint accessed";
    proxy_pass http://backend;
}

This writes to the error log at the default error level.

Logging with Custom Level

Specify a different log level:

location /health {
    error_log_write level=info "message=Health check performed";
    return 200 "OK";
}

Using Variables in Messages

Include request details in your log messages:

location / {
    error_log_write level=info "message=Request: $request_method $request_uri from $remote_addr";
    root /var/www/html;
}

This produces log entries like:

2026/02/12 14:30:45 [info] 1234#1234: *5 error_log_write: Request: GET /index.html from 192.168.1.100

Conditional Logging Examples

The true power of this directive lies in conditional capabilities.

Log When Condition is True (if=)

Log only when a specific condition is met:

location /debug {
    # Only log when ?debug=1 is present
    error_log_write level=warn "message=Debug mode enabled for $remote_addr" "if=$arg_debug";
    return 200 "Debug endpoint";
}

Log When Condition is False (if!=)

Log when a condition is NOT met:

location /admin {
    # Log when Authorization header is missing
    error_log_write level=error "message=Unauthorized access from $remote_addr" "if!=$http_authorization";

    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://admin_backend;
}

Combining Multiple Conditions

Define multiple log entries for different scenarios:

location /api/v2 {
    # Log authenticated requests at info level
    error_log_write level=info "message=Authenticated call: $request_uri" "if=$http_authorization";

    # Log unauthenticated requests at warn level
    error_log_write level=warn "message=Unauthenticated call from $remote_addr" "if!=$http_authorization";

    proxy_pass http://api_backend;
}

Real-World Use Cases

Tracking Authentication Failures

Monitor failed authentication attempts:

server {
    listen 443 ssl;
    server_name secure.example.com;

    # Log all requests missing auth token
    error_log_write level=warn "message=Missing token: $request_uri from $remote_addr" "if!=$http_x_auth_token";

    location /api {
        proxy_pass http://secure_backend;
    }
}

For advanced authentication, see the NGINX JWT authentication module. Also check the NGINX TOTP two-factor authentication module.

Debugging Specific User Agents

Track requests from specific clients:

location / {
    # Log requests from debugging client
    error_log_write level=info "message=Debug client: $request_uri" "if=$http_x_debug_client";

    root /var/www/html;
}

For sophisticated debugging, the NGINX echo module provides shell-style scripting.

Monitoring Slow Backend Indicators

Log when clients signal slow responses:

location /api {
    # Log if client indicates timeout concern
    error_log_write level=warn "message=Timeout warning: upstream=$upstream_addr" "if=$http_x_timeout_warning";

    proxy_pass http://backend;
}

Request Rate Monitoring

Track high-volume request patterns:

location /search {
    # Log every search request with query
    error_log_write level=info "message=Search query: $arg_q from $remote_addr";
    proxy_pass http://search_backend;
}

Geographic Access Logging

When combined with GeoIP modules, log geographic data:

location /checkout {
    # Log checkout attempts with country info
    error_log_write level=info "message=Checkout from $geoip2_country for $remote_addr";
    proxy_pass http://payment_backend;
}

Configuration Inheritance in Detail

Understanding inheritance is crucial for effective use.

http {
    # Logged for ALL requests
    error_log_write level=info "message=Global: $request_uri";

    server {
        listen 80;
        server_name example.com;

        # Logged for all requests to this server
        error_log_write level=info "message=Server: $host";

        location / {
            error_log_write level=warn "message=Root location accessed";
            root /var/www/html;
        }

        location /api {
            # Inherits http and server entries
            error_log_write level=info "message=API: $request_method";
            proxy_pass http://api_backend;
        }
    }
}

A request to /api/users produces three log entries:
1. “Global: /api/users” (from http context)
2. “Server: example.com” (from server context)
3. “API: GET” (from location context)

Performance Considerations

The module is designed for minimal performance impact:

Log Phase Execution: Runs during NGINX’s log phase. This occurs after the response is sent. Logging does not delay responses.

Variable Evaluation: Complex expressions are evaluated at log time. Keep messages simple to minimize overhead.

Condition Evaluation: Uses NGINX’s optimized complex value mechanism.

Disk I/O: Log entries go to the error_log file. Consider I/O for high-traffic servers:

error_log /var/log/nginx/error.log warn;

For alternative logging, see the NGINX pipelog module.

Troubleshooting

Log Entries Not Appearing

  1. Check log level: Your error_log directive must include the target level:
error_log /var/log/nginx/error.log info;
  1. Verify module loading:
nginx -T 2>&1 | grep error_log_write
  1. Check condition evaluation: Empty or “0” values prevent logging.

Quoting Syntax

Parameters with spaces must be quoted:

# CORRECT
error_log_write "message=Request from $remote_addr";

# INCORRECT
error_log_write message=Request from $remote_addr;

notice Level Not Working

Use level=info or level=warn as alternatives.

Comparison with Other Logging Methods

error_log_write vs access_log

The access_log directive logs successful requests in a structured format. It’s ideal for traffic analysis. The error_log_write directive targets specific conditions with custom messages. Use access_log for general traffic. Use error_log_write for targeted debugging.

error_log_write vs Application Logging

Application-level logging requires code changes. The NGINX error_log_write module works at the proxy layer. You can add logging without modifying backends. This is useful when you cannot change application code.

Native NGINX Logging vs error_log_write

Feature Native error_log error_log_write
Global error logging Yes No
Custom messages No Yes
Variable interpolation No Yes
Conditional logging No Yes
Per-location control No Yes
Log level per entry No Yes

Use native error_log for system errors. Use error_log_write for custom application-level logging.

Security Considerations

Follow these security practices:

Avoid Logging Sensitive Data: Never log passwords or tokens:

# BAD
error_log_write "message=Auth: $http_authorization";

# GOOD
error_log_write "message=Auth header present" "if=$http_authorization";

Protect Log Files:

chmod 640 /var/log/nginx/error.log
chown nginx:adm /var/log/nginx/error.log

Rate Awareness: High-frequency logging fills disk quickly. Use conditions to limit volume.

Integration with Log Analysis Tools

The error_log_write entries follow NGINX’s standard format. They work with existing log analysis tools:

  • Logrotate: Standard rotation works without changes
  • Fail2ban: Create filters matching the error_log_write: prefix
  • ELK Stack: Parse using grok patterns for NGINX error logs
  • Prometheus: Use mtail or similar exporters to create metrics

Example fail2ban filter:

[Definition]
failregex = error_log_write: Unauthorized access from <HOST>

Best Practices Summary

  1. Use appropriate log levels: Reserve error for issues. Use info for routine tracking.

  2. Keep messages concise: Include only relevant information.

  3. Use conditions wisely: Avoid logging every request. Target specific scenarios.

  4. Test configurations: Verify with nginx -t before deployment.

  5. Monitor log growth: Conditional logging can generate significant volume.

Conclusion

The NGINX error_log_write module provides flexible conditional logging for your configuration. You gain visibility into request patterns without modifying application code.

Key benefits:

  • Debugging flexibility: Add targeted logging without code changes
  • Conditional control: Log only when conditions match
  • Variable support: Include any NGINX variable in messages
  • Zero response delay: Runs after response is sent

Install the module from the GetPageSpeed repository today.

Source code is on GitHub.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.