Fix IPv4 Geolocation After a Transfer: Step-by-Step
Quick answer: after a transfer, IPv4 geolocation can still show the wrong country (the previous one) because the registry record, the BGP announcement and each commercial GeoIP database are separate systems. Changing the registry does not rewrite the databases. The working fix is to publish your own location file (an RFC 8805 geofeed), reference it from your RIPE Database object and send a correction to each provider, keeping the ticket ids. No provider guarantees a date. Checked on 2026-10-14. The registry steps are in our RIPE NCC transfer guide; this post starts after the block is yours.
Four records, four different questions
| Record | Answers | Who sets it | What it can affect |
|---|---|---|---|
| RIR record (WHOIS) | Who holds the block | The holder, in the registry | Holder identity. country: is not a location |
| BGP origin and ROA | Who announces it, and is that authorized | You, on your routers and in RPKI | Reachability and route validity, not the country |
| Geofeed (RFC 8805) | Where the operator says each prefix is used | You, in a file you host | A hint for providers that read it |
| Commercial GeoIP database | Where that vendor believes the address is | The vendor, from its own sources | What websites and apps see |
The fifth thing, where your servers really are, is what the other four try to model. The RIPE Database documentation says it has never been specified what country: represents, so it cannot be used in any reliable way to map IP addresses to countries, and the RIPE NCC states that it is not a geolocation provider. A block can be registered to a company in one country and run in another, and both records are correct.
Diagnose it with one checklist and one script
| Check | Command | Affects |
|---|---|---|
| CIDR and registry record | whois -h whois.ripe.net -r -T inetnum 192.0.2.0/24 |
Holder, country:, the geofeed: line |
| Geofeed file | curl -s <url from geofeed:> |
The location hint for providers that read it |
| Origin AS and ROA | RIPEstat prefix-overview, rpki-validation |
Routing and route validity, not the country |
| Provider records | The provider's public lookup page | The country your users and partners see |
| Reverse DNS | dig -x 192.0.2.1 |
Mail checks and log readability; no checked source uses it for country |
192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24 and 2001:db8::/32 are documentation prefixes: use them for syntax only, because they are not announced and the RIPE query answers with their non-RIPE parent block. For live output we use DCXV's own prefix and AS204057 (run on 2026-10-14; Linux, whois 5.6, jq 1.8; add -p 43 to whois on a system without a services file):
$ whois -h whois.ripe.net -r -T inetnum 178.23.189.0/24 | grep -E '^(inetnum|netname|country|geofeed)'
inetnum: 178.23.189.0 - 178.23.189.255
netname: CY-DCXV-20210903
country: CY
geofeed: https://dcxv.com/geofeed.csv
$ curl -s 'https://stat.ripe.net/data/prefix-overview/data.json?resource=178.23.189.0/24' | jq -c '.data.asns'
[{"asn":204057,"holder":"DCXV-AS DCXV INTERNATIONAL LTD"}]
$ curl -s 'https://stat.ripe.net/data/rpki-validation/data.json?resource=204057&prefix=178.23.189.0/24' | jq -r '.data.status'
valid
The registry says CY because the holder is a Cyprus company, while the servers are elsewhere. The origin AS and the ROA are valid, and a geofeed: attribute is present. That mix is normal and is exactly why a provider can show a country the registry does not. DCXV publishes its network data here. If you hold a block with this problem, you can Ask About IPv4 Transfer Assistance.
The script below (Python 3.8+, standard library only, tested on 3.14, RIPE-region resources only) prints the registry object, origin AS, RPKI status and the most specific geofeed line for a prefix, using the RIPE Database and RIPEstat. We ran it on DCXV prefixes, on documentation prefixes and on its failure cases. Feed matching was also checked offline on a sample file.
#!/usr/bin/env python3
"""geocheck.py PREFIX - registry country, origin ASN, RPKI status and geofeed line for a prefix.
Python 3.8+, standard library only. Data: RIPE Database REST API and RIPEstat (RIPE region only)."""
import csv, io, ipaddress, json, sys, time, urllib.error, urllib.parse, urllib.request
def get(url):
req = urllib.request.Request(url, headers={'Accept': 'application/json', 'User-Agent': 'geocheck/1.0'})
for attempt in range(4):
try:
with urllib.request.urlopen(req, timeout=20) as r:
return r.read(5_000_000).decode('utf-8-sig', 'replace') # cap the read at 5 MB
except OSError as e: # includes HTTP 429 and connection resets
if isinstance(e, urllib.error.HTTPError) and 400 <= e.code < 500 and e.code != 429:
sys.exit('request refused for %s: %s' % (url, e)) # retrying a 4xx will not help
if attempt == 3:
sys.exit('network error for %s: %s' % (url, e))
time.sleep(2 ** attempt)
def stat(call, **q):
return json.loads(get('https://stat.ripe.net/data/%s/data.json?%s' % (call, urllib.parse.urlencode(q))))['data']
def registry(net):
# best match: the most specific inetnum / inet6num that covers the prefix
t = 'inet6num' if net.version == 6 else 'inetnum'
q = urllib.parse.urlencode([('query-string', str(net)), ('source', 'ripe'), ('type-filter', t),
('flags', 'no-referenced'), ('flags', 'no-irt')])
try:
obj = json.loads(get('https://rest.db.ripe.net/search.json?' + q))['objects']['object'][0]
except (ValueError, KeyError, IndexError):
return None
return {a['name']: a['value'] for a in obj['attributes']['attribute']}
def feed_match(url, net):
# most specific feed entry that covers the prefix; RFC 8805 columns: prefix,country,region,city
best = None
for row in csv.reader(io.StringIO(get(url))):
if not row or row[0].lstrip().startswith('#'):
continue
try:
p = ipaddress.ip_network(row[0].strip(), strict=False)
except ValueError:
continue
if p.version == net.version and net.subnet_of(p) and (best is None or p.prefixlen > best[0].prefixlen):
best = (p, row)
return best[1] if best else None
def main(arg):
try:
net = ipaddress.ip_network(arg, strict=False)
except ValueError:
sys.exit('usage: geocheck.py PREFIX (for example 192.0.2.0/24)')
if net.prefixlen == net.max_prefixlen:
sys.exit('give the prefix, not a single address: a /32 or /128 gives a misleading RPKI result')
if net.prefixlen == 0:
sys.exit('give a real prefix, not the default route')
print('prefix ', net)
reg = registry(net)
top = reg and ((reg.get('netname') or '').startswith('NON-RIPE-NCC-MANAGED')
or (reg.get('inetnum') or '').startswith('0.0.0.0 -') or reg.get('inet6num') == '::/0')
if not reg or top:
print('registry object outside the RIPE NCC: query that RIR instead')
else:
print('registry object ', reg.get('inetnum') or reg.get('inet6num'), '| netname', reg.get('netname'), '| status', reg.get('status'))
print('registry country ', reg.get('country'), '(country: is not a location)')
ov = stat('prefix-overview', resource=str(net))
asns = [str(a['asn']) for a in ov.get('asns', [])]
print('origin ASN ', ', '.join('AS' + a for a in asns) or 'not announced (no origin in RIPEstat)')
for a in asns:
print('RPKI AS%-9s' % a, stat('rpki-validation', resource=a, prefix=str(net))['status'])
url = None if top else (reg or {}).get('geofeed')
if not url:
print('geofeed none (no geofeed: attribute on the best-match object)')
return
print('geofeed URL ', url)
if not url.startswith('https://'):
sys.exit('geofeed URL is not https://, not fetched')
row = feed_match(url, net)
print('geofeed line ', ','.join(row[:4]) if row else 'NO entry covers this prefix')
if __name__ == '__main__':
if len(sys.argv) != 2:
sys.exit('usage: geocheck.py PREFIX')
main(sys.argv[1])
You want valid and a feed line for your prefix. "NO entry covers this prefix" means providers reading your feed skip the block.
Where to send corrections
Use each provider's current channel; fixing one does not fix the others. Documentation checked on 2026-10-14.
| Provider | Channel | What its documentation says |
|---|---|---|
| MaxMind | One-time forms; GeoIP Exchange for operators | Accepts RFC 8805 and RFC 9632 geofeeds, reviewed once per business day; no release date |
| IPinfo | Corrections page; geofeeds | Its community post (2026-02-05): measurement first, geofeeds selectively |
| DB-IP | Report link on the result; batch by contact form | A changed RIR WHOIS record usually triggers a refresh; a geofeed is another route |
| IP2Location | Contact and the data accuracy report | Release dates per licence; no public correction or geofeed procedure found |
| IP report form in Search Help | Updates "may take more than a month"; no individual follow-up |
Publish a geofeed and link it
A geofeed is a CSV file defined by RFC 8805: ip_prefix,alpha2code,region,city. Nested prefixes are allowed and the most specific wins. The RFC asks for UTF-8 and CRLF line breaks, deprecates the postal code, so omit it, and lets consumers treat the data as a hint only. A minimal file with documentation prefixes:
# prefix,country,region,city
192.0.2.0/24,CZ,CZ-10,Prague
2001:db8::/32,CZ,CZ-10,Prague
Host it on HTTPS and add one geofeed: https://example.net/geofeed.csv line to your inetnum or inet6num; the attribute is optional and single. RFC 9632 tells consumers to use the most specific object that has a geofeed and then ignore other hints such as country:. It also defines an optional RPKI signature for the file; we did not verify which providers check it. After a transfer, read the object: a geofeed: line left by the seller points at a file you do not control.
A step plan for the new holder
- Collect evidence. The registry record showing you as holder, the CIDR list, and proof of where the servers run.
- Fix your side. Correct the
geofeed:line, publish the file, run the script. - Submit per provider. One request each, with the feed URL. State the real location only: providers may cross-check it against their own measurements.
- Keep a log. Ticket id, date, provider, prefix, answer.
- Re-check later. Nobody promises a date, so do not plan a launch around one.
Symptom, cause, check, action
| Symptom | Possible cause | Verify | Action |
|---|---|---|---|
| Several providers show the previous country | No geofeed, or not read yet | The script: geofeed: line and a matching row? |
Publish the feed, then one correction per provider |
| One provider follows the registry, not your feed | That provider does not use feeds | Its documentation and a lookup | Send a correction with the feed URL |
| Feed is live, old data remains | Release lag, fetch failure, or measurement disagrees | HTTPS reachable, prefix covered, no conflicting nested row | Ask the provider, keep the ticket, re-check later |
| Only one CDN, bank or payment service is wrong | It runs its own data: Cloudflare, for one, updates its database several times a week and cannot guarantee a match with other providers; risk engines may also weigh other signals | Ask which source it uses | Fix that source, then report to the service |
| Right country, wrong city | The feed has no city, or measurement differs | The city column in the feed | Add region and city, then ask again |

Estimate the risk before you buy
Blacklists and abuse are in the due diligence checklist; here is only geolocation. Look the prefix up at a few providers now, read its inetnum for country: and any geofeed: URL, and ask who controls that file. Agree in the contract that the seller hands over or removes the feed reference at closing.
Next step
Not sure where the gap is? Ask About IPv4 Transfer Assistance or write to ipv4@dcxv.com with the prefix and the symptom. We make no promise about when a provider will update.
