A domain WHOIS query tool implemented in Golang, supporting WHOIS information queries for all publicly queryable TLD domains, IPv4/v6, and ASN.
In compliance with ICANN's "Temporary Specification for gTLD Registration Data" and the EU's "General Data Protection Regulation" (GDPR), when querying domain information, the program only returns essential information (see response examples below) and does not return the owner's contact information, address, phone number, email, and other personal fields.
Demo Sites: - https://whois.ddnsip.cn - https://whois.mmoke.com By immoke
# Install Redis
docker run -d --name redis -p 6379:6379 redis:latest
# Run whois
docker run -d --name whois -p 8043:8043 --link redis:redis jinzeyang/whois
You can download the binary files for your platform from the Release page.
git clone https://github.com/KincaidYang/whois.git
cd whois
go build
This program requires Redis service support. You can refer to https://redis.io/docs/install/install-redis/install-redis-on-linux/ for installation.
vim config.yaml
⚠️ Configuration keys are grouped by function and use camelCase (matching the API response field style). Unknown keys and the pre-v0.9 flat keys fail at startup with a migration hint instead of being silently ignored.
server:
port: 8043 # Server listening port
rateLimit: 60 # Concurrency limit for upstream WHOIS server requests
log:
level: "info" # Log level: debug, info, warn, error (default: info)
cache:
expiration: 3600 # Cache expiration time in seconds (default: 3600)
negativeExpiration: 60 # How long "not found / denied" results are cached, in seconds (default: 60; set negative to disable)
requireRedis: false # false=allow fallback to memory cache when Redis fails, true=Redis must be available or program exits
memoryMaxSize: 10000 # Maximum entries in memory cache; least-recently-used entries are evicted past this (default: 10000)
memoryCleanInterval: 300 # Memory cache cleanup interval in seconds (default: 300)
redis:
addr: "redis:6379" # Redis server address
password: "" # Redis password, leave empty if none
db: 0 # Redis database number
tls: false # Enable TLS when Redis is reached over an untrusted network
tlsSkipVerify: false # Skip certificate verification (not recommended; self-signed certs only)
proxy:
server: "" # Proxy server address; empty disables proxying
username: "" # Proxy server username (if authentication required)
password: "" # Proxy server password (if authentication required)
suffixes: [] # TLD suffixes that use the proxy; ["all"] routes everything through the proxy
bootstrap:
interval: 86400 # RDAP server list refresh interval in seconds; 0 disables fetching (recommended: 86400)
auth:
keys: [] # Accepted API keys. Empty (the default) leaves the service open; one or more keys protect every endpoint except /health and /ready. Clients send a key as "Authorization: Bearer <key>" or "X-API-Key: <key>"
# Entries are bare strings or objects (named, optionally rate-limited):
# keys:
# - "plain-secret" # anonymous key, auto-named key1/key2/...
# - key: "another-secret"
# name: "ci" # display name: appears in the log "client" field and Prometheus metrics
# rateLimit: 120 # per-key rate limit (requests/minute); 0 or omitted = unlimited
batch:
enabled: false # POST /batch bulk-query endpoint (and the MCP batch tool); off by default, best enabled together with auth.keys
maxItems: 10 # maximum queries per batch request
mcp:
localhostProtection: false # DNS-rebinding protection for /mcp: only accept requests whose Host header is localhost. Keep false behind a reverse proxy; set true for direct localhost deployments
Every configuration option can be overridden via environment variables (which take precedence over the config file) — handy for Docker/Kubernetes deployments where mounting a config file is inconvenient:
| Variable | Config key | Default | Description |
|---|---|---|---|
WHOIS_PORT |
server.port |
8043 |
HTTP listen port |
WHOIS_RATE_LIMIT |
server.rateLimit |
100 |
Maximum concurrent requests |
WHOIS_LOG_LEVEL |
log.level |
info |
Log level: debug, info, warn, error |
WHOIS_CACHE_EXPIRATION |
cache.expiration |
3600 |
Cache TTL in seconds |
WHOIS_NEGATIVE_CACHE_EXPIRATION |
cache.negativeExpiration |
60 |
Negative-cache TTL in seconds; negative value disables |
WHOIS_REQUIRE_REDIS |
cache.requireRedis |
false |
true/1 makes startup fail when Redis is unavailable |
WHOIS_MEMORY_MAX_SIZE |
cache.memoryMaxSize |
10000 |
Max entries in the in-memory cache |
WHOIS_MEMORY_CLEAN_INTERVAL |
cache.memoryCleanInterval |
300 |
In-memory cache cleanup interval in seconds |
WHOIS_REDIS_ADDR |
redis.addr |
empty | Redis address; the service falls back to the in-memory cache when unreachable (unless requireRedis) |
WHOIS_REDIS_PASSWORD |
redis.password |
empty | Redis password |
WHOIS_REDIS_DB |
redis.db |
0 |
Redis database number |
WHOIS_REDIS_TLS |
redis.tls |
false |
true/1 enables TLS for Redis |
WHOIS_REDIS_TLS_SKIP_VERIFY |
redis.tlsSkipVerify |
false |
true/1 skips certificate verification (not recommended) |
WHOIS_PROXY_SERVER |
proxy.server |
empty | Proxy URL; empty disables proxying |
WHOIS_PROXY_USERNAME |
proxy.username |
empty | Proxy username |
WHOIS_PROXY_PASSWORD |
proxy.password |
empty | Proxy password |
WHOIS_PROXY_SUFFIXES |
proxy.suffixes |
empty | TLDs queried through the proxy, comma-separated (all proxies everything) |
WHOIS_BOOTSTRAP_INTERVAL |
bootstrap.interval |
0 (disabled) |
IANA RDAP list refresh interval in seconds; the sample config ships 86400 |
WHOIS_AUTH_KEYS |
auth.keys |
empty | API keys, comma-separated (bare keys only; naming / per-key limits need the config file) |
WHOIS_BATCH_ENABLED |
batch.enabled |
false |
true/1 enables the bulk-query endpoint |
WHOIS_BATCH_MAX_ITEMS |
batch.maxItems |
10 |
Maximum queries per batch request |
WHOIS_MCP_LOCALHOST_PROTECTION |
mcp.localhostProtection |
false |
true/1 enables DNS-rebinding protection for /mcp |
Boolean variables accept only true and 1; anything else is treated as false. Numeric variables that fail to parse are silently ignored, leaving the config-file/default value in place.
Environment-only deployment example (no config file mounted):
docker run -d --name whois -p 8043:8043 \
-e WHOIS_REDIS_ADDR=redis:6379 \
-e WHOIS_BOOTSTRAP_INTERVAL=86400 \
-e WHOIS_AUTH_KEYS=secret1,secret2 \
--link redis:redis jinzeyang/whois
Configuration Notes:
- Redis Configuration: Redis is recommended for better performance and multi-instance cache sharing
- Cache Expiration: Adjust based on query frequency, 3600 seconds recommended
- Memory Cache: Fallback when Redis is unavailable; evicts least-recently-used (LRU) entries once full
- Negative Cache: Briefly caches "not found / denied" results to avoid repeatedly hitting upstream for missing resources; defaults to 60 seconds
- Concurrency Limit: Controls request frequency to upstream servers to avoid rate limiting
- Proxy Configuration: Some TLDs may require proxy access
- Log Level: debug logs every cache hit and upstream query dispatch — noisy under load; info is recommended for production
- Bootstrap Interval: On startup the service immediately fetches the latest RDAP server list from IANA, then refreshes on this interval; compiled-in data serves as fallback if the fetch fails
- API Authentication: Disabled by default. Configuring auth.keys enables it; requests without a valid key get a 401 (RFC 9457 problem+json). Only /health and /ready are exempt so liveness/readiness probes keep working
- Key naming and per-key rate limits: The object form of auth.keys gives each key a display name and a rate limit. The name labels the caller in the request logs (client field) and in the whois_client_requests_total{client,status_code} Prometheus metric; the limit is a token bucket (requests/minute, a full minute's budget may be spent at once) answering over-budget requests with 429 + Retry-After. Batches are charged per item: a batch of N queries costs N tokens
- Batch queries: Off by default. Best enabled together with auth.keys — an open instance offering bulk queries multiplies how fast it can be abused against upstream registries
⚠️ Warning: The rate limit applies to requests from this program to WHOIS servers, not requests from users to this program. For example, if you set the limit to 50, the program will not exceed 50 requests/second to registry WHOIS servers, but user requests to this program are unlimited. Please use Nginx or other tools to rate-limit this program, or configure a per-key
rateLimit, to prevent malicious requests.
./whois
Note: The program listens on port 8043 by default.
The service provides the following health check endpoints:
| Endpoint | Description |
|---|---|
GET /health |
Liveness probe - returns 200 if service is running |
GET /ready |
Readiness probe - checks cache and capacity status |
GET /info |
Runtime information - version, uptime, Go version, etc. |
GET /metrics |
Prometheus metrics - request count, latency, cache hit rate, upstream query duration |
GET /openapi.json |
OpenAPI 3.1 specification - machine-readable description of all endpoints and response schemas |
POST /mcp |
MCP Streamable HTTP endpoint - for AI assistant integration |
POST /batch |
Bulk queries - multiple domains/IPs/ASNs in one request (off by default, see batch.enabled) |
You can use systemd or other tools to set up this program as a daemon process to auto-start after system reboot.
vim /etc/systemd/system/whois.service
[Unit]
Description=whois
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/path/to/whois/whois
WorkingDirectory=/path/to/whois
Restart=on-failure
[Install]
WantedBy=multi-user.target
GET requests
After deployment, access http://ip:port/domain-or-ip-or-asn via browser. Default port is 8043, e.g., http://1.2.3.4:8043/example.com
Besides the auto-detecting root path, RFC 9082-style typed paths are available, suited to programmatic use — a resource of the wrong type returns 400 instead of being interpreted as another type:
curl http://localhost:8043/domain/example.com
curl http://localhost:8043/ip/192.0.2.1
curl http://localhost:8043/autnum/205794 # as205794 / AS205794 also accepted
IP queries accept CIDR prefixes (on both the root path and /ip/):
curl http://localhost:8043/ip/192.0.2.0/24
curl http://localhost:8043/2001:db8::/32
An OpenAPI 3.1 description of the service is available at /openapi.json, covering every endpoint, the response schemas (RDAP vocabulary) and the error format. It can be imported directly into Postman, Swagger UI and similar tools.
X-Cache header describing the cache outcome: HIT (served from the server cache), MISS (fetched upstream), or REFRESH (forced upstream by ?refresh), plus Cache-Control: public, max-age=<cache seconds> for client/CDN caching.ETag; send it back as If-None-Match: <etag> for conditional revalidation — unchanged content is answered with 304 Not Modified and no body. /openapi.json supports this too.Access-Control-Allow-Origin: *, so the API can be called cross-origin from browser frontends directly.Internationalized domain names (IDN, including Unicode domains with non-ASCII characters) can be queried directly; the program converts them to Punycode automatically, e.g.
http://1.2.3.4:8043/例子.cn.
curl http://localhost:8043/example.com
Response: ```json { "objectClassName": "domain", "ldhName": "example.com", "registrar": "RESERVED-Internet Assigned Numbers Authority", "registrarIanaId": "376", "status": [ "client delete prohibited", "client transfer prohibited", "client update prohibited" ], "registrationDate": "1995-08-14T04:00:00Z", "expirationDate": "2026-08-13T04:00:00Z", "lastChangedDate": "2025-08-14T07:01:34Z", "nameservers": [ "a.iana-servers.net", "b.iana-servers.net" ], "secureDNS": { "delegationSigned": true, "dsData": [ { "keyTa
$ claude mcp add whois \
-- python -m otcore.mcp_server <graph>