rdns: NGINX HTTP rDNS module
Debian/Ubuntu installation
These docs apply to the APT package nginx-module-rdns provided by the GetPageSpeed Extras repository.
- Configure the APT repository as described in APT repository setup.
- 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 |
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
}
}
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
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:
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.