Skip to content

rdns: NGINX HTTP rDNS module

Debian/Ubuntu installation

These docs apply to the APT package nginx-module-rdns provided by the GetPageSpeed Extras repository.

  1. Configure the APT repository as described in APT repository setup.
  2. Install the module:
sudo apt-get update
sudo apt-get install nginx-module-rdns
Show suites and architectures
| Distro   | Suite             | Component   | Architectures   |
|----------|-------------------|-------------|-----------------|
| debian   | bookworm          | main        | amd64, arm64    |
| debian   | bookworm-mainline | main        | amd64, arm64    |
| debian   | trixie            | main        | amd64, arm64    |
| debian   | trixie-mainline   | main        | amd64, arm64    |
| ubuntu   | focal             | main        | amd64, arm64    |
| ubuntu   | focal-mainline    | main        | amd64, arm64    |
| ubuntu   | jammy             | main        | amd64, arm64    |
| ubuntu   | jammy-mainline    | main        | amd64, arm64    |
| ubuntu   | noble             | main        | amd64, arm64    |
| ubuntu   | noble-mainline    | main        | amd64, arm64    |

Test Build

Reverse DNS lookup module for NGINX with hostname-based access control.

Perform asynchronous reverse DNS (PTR) lookups on client IP addresses and use the resolved hostname for access control decisions, logging, or conditional request handling.

Features

  • Asynchronous DNS Resolution - Non-blocking PTR lookups using NGINX's core resolver
  • Double Verification Mode - Optional forward DNS verification to prevent DNS spoofing
  • Regex-Based Access Control - Allow or deny requests based on hostname patterns
  • Full Context Support - Works in http, server, location, and if blocks
  • Efficient Caching - Leverages NGINX's resolver cache (up to 30 seconds or DNS TTL)
  • Dynamic Module Support - Build as static or dynamic module
  • IPv4 and IPv6 Support - Works with both address families

Quick Start

http {
    # Define a DNS resolver (required)
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    server {
        listen 80;

        location / {
            # Enable reverse DNS lookup
            rdns on;

            # Allow only requests from *.google.com
            rdns_allow \.google\.com$;

            # Deny requests from known bad actors
            rdns_deny \.spam\.example\.com$;

            # Use the hostname in your application
            proxy_set_header X-Client-Hostname $rdns_hostname;
            proxy_pass http://backend;
        }
    }
}

Directives

rdns

Enables or disables reverse DNS lookups.

Syntax rdns on | off | double
Default off
Context http, server, location, if
Phase rewrite

Values:

Value Description
off Disable rDNS lookup. $rdns_hostname will be -
on Perform a single PTR lookup
double Perform PTR lookup, then verify with forward A/AAAA lookup

Double mode provides protection against DNS spoofing by verifying that the resolved hostname points back to the original client IP address. If verification fails, $rdns_hostname is set to not found.

rdns_allow

Allow access if the resolved hostname matches the pattern.

Syntax rdns_allow regex
Default -
Context http, server, location
Phase access

The pattern is a case-insensitive PCRE regular expression.

rdns_deny

Deny access (return 403) if the resolved hostname matches the pattern.

Syntax rdns_deny regex
Default -
Context http, server, location
Phase access

The pattern is a case-insensitive PCRE regular expression.

Variables

$rdns_hostname

Contains the result of the reverse DNS lookup.

Value Meaning
hostname Successfully resolved hostname
not found Lookup failed, timed out, or double verification failed
- rDNS is disabled in this context

Usage Examples

Verify Search Engine Crawlers

Ensure requests claiming to be from Googlebot actually originate from Google:

location / {
    resolver 8.8.8.8;

    # Only perform rDNS for requests claiming to be Googlebot
    if ($http_user_agent ~* "Googlebot") {
        rdns double;
    }

    # Allow verified Googlebot
    rdns_allow \.googlebot\.com$;
    rdns_allow \.google\.com$;

    # Your normal configuration
    proxy_pass http://backend;
}

Block Requests by Hostname

location / {
    resolver 8.8.8.8;
    rdns on;

    # Block known spam sources
    rdns_deny \.spam\.example\.com$;
    rdns_deny \.malicious\.net$;

    proxy_pass http://backend;
}

Log Client Hostnames

http {
    resolver 8.8.8.8;

    log_format with_hostname '$remote_addr - $rdns_hostname - $remote_user [$time_local] '
                             '"$request" $status $body_bytes_sent '
                             '"$http_referer" "$http_user_agent"';

    server {
        access_log /var/log/nginx/access.log with_hostname;

        location / {
            rdns on;
            proxy_pass http://backend;
        }
    }
}

Conditional Logic Based on Hostname

location / {
    resolver 8.8.8.8;
    rdns on;

    # Set a variable based on hostname
    set $is_internal "no";
    if ($rdns_hostname ~ \.internal\.company\.com$) {
        set $is_internal "yes";
    }

    # Use in proxy headers
    proxy_set_header X-Is-Internal $is_internal;
    proxy_set_header X-Client-Hostname $rdns_hostname;
    proxy_pass http://backend;
}

Different Rules for Different Locations

server {
    resolver 8.8.8.8;

    # Public API - no rDNS
    location /api/public {
        proxy_pass http://api-backend;
    }

    # Admin area - strict hostname verification
    location /admin {
        rdns double;
        rdns_allow \.admin\.company\.com$;
        proxy_pass http://admin-backend;
    }

    # General content - log hostnames
    location / {
        rdns on;
        proxy_pass http://web-backend;
    }
}

Access Control Behavior

Rule Evaluation

  1. Rules are evaluated in the order they appear in the configuration
  2. The first matching rule determines the outcome:
  3. rdns_allow match: Request is allowed (processing continues)
  4. rdns_deny match: Request is denied with 403 Forbidden
  5. If no rules match, the request is allowed

Rule Inheritance

  • Child contexts (locations) inherit rules from parent contexts only if they don't define their own rules
  • Once a child defines any rdns_allow or rdns_deny rule, parent rules are not inherited
server {
    rdns_allow \.trusted\.com$;  # Server-level rule

    location /public {
        # Inherits server-level rdns_allow rule
    }

    location /private {
        rdns_deny \.untrusted\.com$;  # Has own rule
        # Does NOT inherit server-level rdns_allow
    }
}

Important Notes

Resolver Configuration

A resolver directive must be defined in the same context or a parent context when using rdns on or rdns double. NGINX will fail to start with the error no core resolver defined for rdns if this requirement is not met.

## Good: resolver defined
server {
    resolver 8.8.8.8;
    location / {
        rdns on;  # Works
    }
}

## Bad: no resolver
server {
    location / {
        rdns on;  # Error: no core resolver defined
    }
}

Named Locations

Avoid enabling rDNS in configurations that use named locations, as this can cause redirect loops:

## Problematic configuration
server {
    rdns on;

    location / {
        error_page 404 = @fallback;
    }

    location @fallback {
        # Loop! The rDNS lookup restarts request processing
    }
}

## Fixed configuration
server {
    rdns on;

    location / {
        error_page 404 = @fallback;
    }

    location @fallback {
        rdns off;  # Disable rDNS in named location
        # ...
    }
}

Performance Considerations

  • Each rDNS lookup adds latency to request processing
  • Use conditional enabling (via if blocks) to limit lookups to specific user agents or conditions
  • The resolver cache helps reduce repeated lookups for the same IP
  • Consider double mode only when spoofing protection is necessary

Troubleshooting

"no core resolver defined for rdns"

Add a resolver directive in the same or parent context:

resolver 8.8.8.8;

$rdns_hostname is always "not found"

  1. Verify the resolver is reachable from your server
  2. Check resolver timeout settings
  3. If using rdns double, ensure the PTR record's hostname has a valid A/AAAA record pointing back to the client IP
  4. Check NGINX error logs for resolver errors

$rdns_hostname is always "-"

The rdns directive is either: - Not enabled in the current context - Set to off - Inside an if block whose condition is false

403 Forbidden responses

An rdns_deny rule matched the client's hostname. Check your deny patterns and the actual hostname being resolved.