rdns: NGINX HTTP rDNS module
Installation
You can install this module in any RHEL-based distribution, including, but not limited to:
- RedHat Enterprise Linux 7, 8, 9 and 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 and Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-rdns
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-rdns
Enable the module by adding the following at the top of /etc/nginx/nginx.conf:
load_module modules/ngx_http_rdns_module.so;
This document describes nginx-module-rdns v1.2.0 released on Feb 02 2026.
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, andifblocks - 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
- Rules are evaluated in the order they appear in the configuration
- The first matching rule determines the outcome:
rdns_allowmatch: Request is allowed (processing continues)rdns_denymatch: Request is denied with403 Forbidden- 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_alloworrdns_denyrule, 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
}
}
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
ifblocks) to limit lookups to specific user agents or conditions - The resolver cache helps reduce repeated lookups for the same IP
- Consider
doublemode only when spoofing protection is necessary
Troubleshooting
"no core resolver defined for rdns"
Add a resolver directive in the same or parent context:
nginx
resolver 8.8.8.8;
$rdns_hostname is always "not found"
- Verify the resolver is reachable from your server
- Check resolver timeout settings
- If using
rdns double, ensure the PTR record's hostname has a valid A/AAAA record pointing back to the client IP - 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.
GitHub
You may find additional configuration tips and documentation for this module in the GitHub repository for nginx-module-rdns.