When a client connects to your NGINX server, you see an IP address. However, IP addresses tell you nothing about who owns them. A request claiming to be from Googlebot might actually come from a scraper. Traffic from a “trusted partner” could originate from a compromised server. The NGINX reverse DNS module solves this problem by resolving client IP addresses to hostnames, enabling hostname-based access control decisions.
The nginx-module-rdns package is available exclusively from the GetPageSpeed NGINX Extras repository. This remarkable NGINX reverse DNS module is a complete clean-room implementation and drop-in replacement for the original open source ngx_http_rdns module, which was abandoned years ago and no longer compiles with modern NGINX versions. Our implementation brings this essential functionality back to life with full compatibility for the latest NGINX releases, including dynamic module support.
What Is Reverse DNS and Why Does It Matter?
Reverse DNS (rDNS) is the process of resolving an IP address back to a hostname. While forward DNS maps example.com to 93.184.216.34, reverse DNS maps 93.184.216.34 back to a hostname like server1.example.com.
For system administrators, reverse DNS verification serves several critical security purposes:
- Crawler verification: Confirm that requests claiming to be from search engines actually originate from their networks
- Partner authentication: Verify that traffic comes from legitimate partner hostnames
- Threat identification: Log and analyze the hostnames of suspicious traffic sources
- Access control: Allow or deny requests based on hostname patterns instead of maintaining IP lists
The challenge with IP-based access control is that IP addresses change frequently. Cloud providers reassign IPs, companies change hosting providers, and maintaining accurate IP allowlists becomes an ongoing burden. Hostname-based verification offers a more stable alternative because organizations control their DNS records.
How the NGINX rDNS Module Works
The NGINX reverse DNS module (ngx_http_rdns) performs asynchronous reverse DNS lookups during request processing. Here is how it operates:
- Client connects: A request arrives from IP address
66.249.66.1 - PTR lookup: The module queries DNS for the PTR record of
1.66.249.66.in-addr.arpa - Hostname resolved: DNS returns
crawl-66-249-66-1.googlebot.com - Access decision: Your configuration rules match against this hostname
The module integrates with NGINX’s core resolver, which means lookups are non-blocking. Your server continues processing other requests while waiting for DNS responses.
Single vs Double Verification Mode
The module offers two verification modes:
Single mode (rdns on) performs only the PTR lookup. This is faster but vulnerable to DNS spoofing. An attacker controlling their PTR records could set them to claim any hostname.
Double mode (rdns double) adds forward verification. After resolving the PTR record to a hostname, the module performs an A or AAAA lookup on that hostname. Only if the forward lookup returns the original client IP does verification succeed.
For security-critical applications, always use double mode. The additional DNS query adds minimal latency but provides strong spoofing protection.
Installation on RHEL, CentOS, Rocky Linux, and AlmaLinux
The NGINX reverse DNS module is exclusively available through the GetPageSpeed NGINX Extras repository, which provides pre-built packages for all Enterprise Linux distributions. This is the only source for a maintained, modern build of this module.
Enable the GetPageSpeed Repository
First, install the repository configuration:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
Install the Module
Install the rDNS module package:
sudo dnf install nginx-module-rdns
This installs the compiled module to /usr/lib64/nginx/modules/.
Load the Module
Add the following line to your /etc/nginx/nginx.conf at the top, before any http block:
load_module modules/ngx_http_rdns_module.so;
Verify the configuration and reload NGINX:
sudo nginx -t && sudo systemctl reload nginx
Drop-In Replacement for the Abandoned Original
If you previously used the original ngx_http_rdns module before it was abandoned, you’ll find our NGINX reverse DNS module is a complete drop-in replacement. All existing configurations work without modification:
- Same directive names:
rdns,rdns_allow,rdns_deny - Same variable:
$rdns_hostname - Same contexts:
http,server,location,if - Same behavior and semantics
Simply install from the GetPageSpeed repository and your existing configurations will work immediately. No migration needed.
Configuration Reference
The module provides three directives and one variable.
The rdns Directive
The rdns directive enables or disables reverse DNS lookups.
| Property | Value |
|---|---|
| Syntax | rdns on | off | double |
| Default | off |
| Context | http, server, location, if |
Values explained:
off: Disable rDNS lookup. The$rdns_hostnamevariable contains-on: Perform a single PTR lookupdouble: Perform PTR lookup followed by forward A/AAAA verification
The rdns_allow Directive
The rdns_allow directive permits access when the resolved hostname matches a pattern.
| Property | Value |
|---|---|
| Syntax | rdns_allow regex |
| Default | none |
| Context | http, server, location |
The pattern is a case-insensitive PCRE regular expression. Use proper escaping for literal dots.
The rdns_deny Directive
The rdns_deny directive blocks access (returns 403 Forbidden) when the hostname matches.
| Property | Value |
|---|---|
| Syntax | rdns_deny regex |
| Default | none |
| Context | http, server, location |
The $rdns_hostname Variable
This variable contains the result of the reverse DNS lookup:
| Value | Meaning |
|---|---|
| hostname | Successfully resolved and verified hostname |
not found |
Lookup failed, timed out, or double verification failed |
- |
rDNS is disabled in the current context |
Practical Configuration Examples
Verifying Search Engine Crawlers
One of the most valuable uses of the NGINX reverse DNS module is verifying legitimate search engine crawlers. Many bots falsely identify as Googlebot to bypass rate limiting or access restricted content. Google officially recommends using reverse DNS verification to confirm Googlebot identity.
http {
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
server {
listen 80;
server_name example.com;
location / {
# Only verify requests claiming to be Googlebot
if ($http_user_agent ~* "Googlebot") {
rdns double;
}
# Allow verified Google crawlers
rdns_allow \.googlebot\.com$;
rdns_allow \.google\.com$;
proxy_pass http://backend;
}
}
}
This configuration performs rDNS verification only when the User-Agent contains “Googlebot”. Legitimate Google crawlers resolve to hostnames ending in .googlebot.com or .google.com. The double mode ensures spoofed PTR records are rejected.
Blocking Malicious Hostnames
You can block requests from known malicious hostname patterns:
http {
resolver 8.8.8.8 valid=300s;
server {
listen 80;
server_name example.com;
location /api {
rdns on;
# Block known bad actors
rdns_deny \.spam\.example\.net$;
rdns_deny \.malware-host\.com$;
rdns_deny ^crawler\d+\.aggressive-bot\.org$;
proxy_pass http://api-backend;
}
}
}
Logging Client Hostnames
Even without access control rules, logging resolved hostnames provides valuable security intelligence:
http {
resolver 8.8.8.8 valid=300s;
log_format detailed '$remote_addr - $rdns_hostname [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/access.log detailed;
location / {
rdns on;
proxy_pass http://backend;
}
}
}
Your logs now include hostnames like crawl-66-249-66-1.googlebot.com instead of just IP addresses, making log analysis more informative.
Restricting Admin Access by Hostname
Protect administrative areas by requiring connections from specific hostnames. For additional protection, consider combining this with NGINX basic authentication:
server {
resolver 8.8.8.8 valid=300s;
resolver_timeout 5s;
location /admin {
rdns double;
# Only allow from company network hostnames
rdns_allow \.internal\.company\.com$;
rdns_allow ^vpn-\d+\.company\.com$;
proxy_pass http://admin-backend;
}
location / {
# Public access, no rDNS needed
proxy_pass http://public-backend;
}
}
Passing Hostname to Backend Applications
Forward the resolved hostname to your application for additional processing:
location / {
resolver 8.8.8.8 valid=300s;
rdns on;
proxy_set_header X-Client-Hostname $rdns_hostname;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://backend;
}
Your application can then implement its own hostname-based logic using the X-Client-Hostname header.
Understanding Rule Evaluation
The module evaluates rdns_allow and rdns_deny rules in the order they appear in your configuration. The first matching rule determines the outcome:
- If
rdns_allowmatches first, the request proceeds - If
rdns_denymatches first, NGINX returns 403 Forbidden - If no rules match, the request proceeds
This means rule order matters:
# This configuration allows .google.com but denies everything else
location /api {
rdns double;
rdns_allow \.google\.com$;
rdns_deny .*; # Deny all other hostnames
}
Rule Inheritance Behavior
Rules inherit from parent contexts only when child contexts define no rules:
server {
rdns_allow \.trusted\.com$; # Server-level rule
location /public {
# Inherits server-level rdns_allow rule
rdns on;
}
location /private {
rdns double;
rdns_deny \.untrusted\.com$; # Has own rule
# Does NOT inherit server-level rdns_allow
}
}
Performance Considerations
Reverse DNS lookups add latency to request processing. Consider these factors when deploying the NGINX reverse DNS module:
DNS Query Latency
Each rDNS lookup requires at least one DNS query. In double mode, two queries are needed. Query times typically range from 1-50ms depending on your resolver distance and DNS server performance.
Mitigation strategies:
- Use fast, local DNS resolvers
- Configure appropriate
resolver_timeoutvalues - Enable rDNS only for specific locations or conditions
Resolver Caching
NGINX caches DNS responses according to the valid parameter in your resolver directive:
resolver 8.8.8.8 valid=300s;
This caches responses for 300 seconds, reducing repeated lookups for the same IP addresses. However, this also means hostname changes take time to propagate.
Conditional Enabling
Avoid performing rDNS on every request. Use conditions to limit lookups:
# Only verify suspicious user agents
if ($http_user_agent ~* "(bot|crawler|spider)") {
rdns double;
}
High-Traffic Considerations
On high-traffic servers, DNS query volume can become significant. Ensure your DNS resolver infrastructure can handle the load, or consider implementing rDNS verification at a rate-limited layer before NGINX. For comprehensive traffic management, see our guide on NGINX rate limiting.
Troubleshooting Common Issues
Error: “no core resolver defined for rdns”
This error occurs when you enable rdns on or rdns double without defining a resolver:
# Wrong: no resolver defined
server {
location / {
rdns on; # Error!
}
}
# Correct: resolver defined in same or parent context
server {
resolver 8.8.8.8;
location / {
rdns on; # Works
}
}
Variable $rdns_hostname Always Shows “not found”
Several conditions cause “not found” results:
- No PTR record exists: The client IP has no reverse DNS entry
- DNS timeout: The resolver didn’t respond within
resolver_timeout - Double verification failed: PTR resolved but forward lookup didn’t match the original IP
- Resolver unreachable: Your NGINX server cannot reach the configured DNS resolver
Check NGINX error logs for resolver-related messages:
tail -f /var/log/nginx/error.log | grep -i resolver
Variable $rdns_hostname Always Shows “-“
The dash indicates rDNS is disabled. Verify that:
- The
rdnsdirective is set toonordouble - The directive is in the correct context (location, server, or http)
- Any
ifconditions containing therdnsdirective evaluate to true
Redirect Loops with Named Locations
Enabling rDNS globally can cause issues with named locations:
# Problematic: causes redirect loops
server {
rdns on;
location / {
error_page 404 = @fallback;
}
location @fallback {
# rDNS restarts request processing, causing loop
}
}
# Fixed: disable rDNS in named location
server {
rdns on;
location / {
error_page 404 = @fallback;
}
location @fallback {
rdns off; # Prevents loop
# handle fallback
}
}
Security Best Practices
Follow these recommendations for secure rDNS deployment:
Always Use Double Mode for Access Control
Single mode PTR lookups are trivially spoofable. Any attacker controlling their IP’s reverse DNS can claim any hostname. Always use rdns double when making access control decisions:
# Insecure: single mode is spoofable
rdns on;
rdns_allow \.trusted\.com$;
# Secure: double mode verifies forward DNS
rdns double;
rdns_allow \.trusted\.com$;
Combine with Other Security Measures
Reverse DNS verification works best as part of defense in depth. Consider combining it with TLS hardening and other security layers:
location /api {
# Layer 1: Rate limiting
limit_req zone=api burst=10;
# Layer 2: rDNS verification
rdns double;
rdns_allow \.partner\.com$;
# Layer 3: API key validation in backend
proxy_pass http://api-backend;
}
Monitor DNS Resolution Failures
High rates of “not found” results may indicate:
- Attackers probing your server from IPs without rDNS
- DNS infrastructure issues
- Legitimate clients without rDNS (some are valid)
Log and alert on unusual patterns:
log_format rdns_failures '$remote_addr - $rdns_hostname - $request';
map $rdns_hostname $loggable {
"not found" 1;
default 0;
}
access_log /var/log/nginx/rdns_failures.log rdns_failures if=$loggable;
Verify Crawler Patterns Regularly
Search engines occasionally change their crawler hostnames. Verify your allow patterns remain accurate:
- Google:
*.googlebot.com,*.google.com - Bing:
*.search.msn.com - Yandex:
*.yandex.com,*.yandex.ru,*.yandex.net
Consult each search engine’s official documentation for current crawler verification guidance.
Testing Your Configuration
After configuring the module, verify it works correctly.
Check Configuration Syntax
sudo nginx -t
Test from Known Hostnames
If you have access to a server with known rDNS, make requests from it:
curl -H "Host: example.com" http://your-server/
Verify Variable Values
Create a test location that returns the resolved hostname:
location /rdns-test {
resolver 8.8.8.8 valid=60s;
rdns on;
return 200 "Your hostname: $rdns_hostname\n";
add_header Content-Type text/plain;
}
Access this endpoint to see what hostname your IP resolves to:
curl http://example.com/rdns-test
Simulate Crawler Verification
Test your crawler verification logic by checking against known Googlebot IPs. Google publishes its crawler IP ranges, allowing you to verify your configuration catches legitimate crawlers.
Conclusion
The NGINX reverse DNS module is an exceptional tool for hostname-based access control. By verifying client hostnames through DNS lookups, you can authenticate legitimate crawlers, restrict access to trusted partners, and gain deeper visibility into who accesses your server.
This module is exclusively available from the GetPageSpeed NGINX Extras repository — the only source for a modern, maintained build that works with current NGINX versions. As a complete drop-in replacement for the abandoned original open source module, it breathes new life into this essential security functionality.
Key takeaways:
- Use double mode for any security-critical access control
- Conditional enabling prevents unnecessary DNS queries
- Combine with other security measures for defense in depth
- Monitor resolution failures to detect issues and attacks
- Available exclusively from GetPageSpeed — install with
dnf install nginx-module-rdns
Get started today by enabling the GetPageSpeed repository and installing the NGINX reverse DNS module.

