Free DynDNS

Documentation

Everything needed to point a router or a script at bhdns.de: the update protocol, settings for AVM FRITZ!Box and Telekom Speedport, ready-made scripts, IPv6 handling and the limits of the free tier.

Last updated: 11 October 2026, 20:03 CEST

Documentation

Quick start

Claim a subdomain, then point your router or a script at the update URL. The change is written to the primary nameserver at once. The secondary nameserver receives it on the next zone transfer, and resolvers may keep returning the old address until their TTL expires — so how fast the change is visible everywhere also depends on when the secondary has the zone. Both IPv4 (A) and IPv6 (AAAA) are supported.

You can choose the domain in the form — bhdns.de or sbdns.de. The name you pick is created as <label>.<domain>, the same label may exist in both domains, and each domain has its own limit of five free subdomains. Address detection always runs through v4.bhdns.de / v6.bhdns.de: those names force the request to use IPv4 or IPv6 respectively, so the server can report the address it sees. A browser, curl, PowerShell or a NAS on your network all get the same answer, and the names are independent of the domain you pick.

  1. Create an account and confirm the 6-digit code — sign up.
  2. Add a subdomain such as my-home.
  3. Copy the update token from the subdomain dialog — it is shown once.
  4. Enter URL, domain, username and token into your router (below) or run a script.

Values your client needs

Update addressupdate.bhdns.de/nic/update — one address for every client. Routers that ask for the address, the protocol and the port separately take it bare, exactly as written; a script or an app that takes a whole URL writes https://update.bhdns.de/nic/update. It is not the bare domain on purpose: a router that resolved bhdns.de before the domain was delegated may still hold the registrar's old 127.0.0.1 in its cache and send the update to itself, while nothing can be cached for a name that did not exist yet.
Domain / hostnamemy-home.bhdns.de
Usernamemy-home.bhdns.de (the same full hostname)
Passwordyour update token, e.g. bhdns_9f2c… — 30 characters, short enough for the FRITZ!Box field (32) and the Speedport (50)

Protocol and port are not optional. The service answers on HTTPS, port 443. Plain HTTP is only redirected to HTTPS and routers do not follow that redirect, so an http:// URL or the wrong port fails with an error the router shows as "not registered".

DynDNS2

Update protocol

The endpoint follows the dyndns2 convention that routers and clients already speak, with HTTP Basic authentication. GET and POST both work.

GET https://update.bhdns.de/nic/update?hostname=my-home.bhdns.de&myip=203.0.113.47&myipv6=2a01:4f8::1
Authorization: Basic base64("my-home.bhdns.de:<token>")
ParameterMeaning
hostnameFull hostname. Several names may be comma-separated.
myipIPv4 address → written as an A record. May be a comma-separated list: every address goes to the record of its own family, so an IPv6 address here also works (the Speedport Smart 4 sends myip=<ipv6>,<ipv4>).
myipv6IPv6 address → written as an AAAA record.

Send both parameters to keep both records current. If neither is sent, the address of the request itself is used. Only the changed family is rewritten — the other record stays untouched.

When several names are sent, each name is authenticated with the same token. A token belongs to one subdomain, so only that subdomain is updated; the other names are answered with badauth (the name exists, the token does not match) or nohost (no such name). The check therefore never touches a name the token does not own.

Answers

AnswerMeaning
good <ip>The record was updated.
nochg <ip>The address is already current; nothing was written.
badauthWrong token, or none sent.
nohostThe hostname does not exist on this service.
notfqdnNo hostname was given and the token did not identify one. If the hostname parameter is missing, the name is taken from the token — a token belongs to exactly one subdomain.
abuseA limit was hit — see Limits.
911The address could not be published — it was unreadable (for example text in myip), or it lies in a reserved, private or otherwise unroutable range (RFC 6890). The dashboard log names the range.
Address families

IPv6 and AAAA records

Routers such as the FRITZ!Box and the Speedport can send IPv4 and IPv6 in the same update — whether they do depends on the model, the firmware and the settings — so a subdomain often gets both an A and an AAAA record. If you do not want the IPv6 address published — for example because your provider changes the prefix and the AAAA record or the firewall rules are then not updated — open the subdomain in the dashboard and choose Remove IPv6.

That is a setting for this subdomain, not a one-off deletion: from then on the server ignores the IPv6 address your router sends, so the AAAA record does not come back with the next update. Choose Allow IPv6 to undo it — the record returns with the next update from your router.

Every subdomain keeps at least one address. If it has no IPv4 address yet, IPv6 cannot be switched off: add an IPv4 address first, otherwise the name would stop resolving entirely.

What IPv6 needs in order to work

An AAAA record only publishes the address — it does not make the address reachable. Two more things have to be true, and neither of them is in our hands:

  • IPv6 normally needs no address translation, and the device usually has a globally routable address. Reachability is decided by routing and by the router’s IPv6 firewall: incoming connections have to be allowed for the device and the service. How that is configured depends on the router. An IPv4 port-forwarding rule does not create the equivalent IPv6 rule.
  • The router decides which address it reports. Some report the address of the device, others report the router’s own address. If it reports its own and the router does not forward the traffic, the AAAA record points nowhere even though it looks correct.

IPv6 matters most when your connection has no directly reachable public IPv4 address (DS-Lite, CGNAT) or when clients can only use IPv6. It is not the only way to reach a service at home: a VPN or a tunnel works without it, and a client on an IPv6-only network can still reach IPv4 destinations when the network provides NAT64/DNS64.

If your router reports its own address, or does not send IPv6 at all, run the updater on the device itself — see Other routers. The AAAA record then carries the address of the device, which is what a client has to reach.

To find out what is true in your case, test from a device on another network (for example a phone with Wi-Fi switched off) and compare IPv4 with IPv6. If your service is reachable over public IPv4 and you only need IPv4 access, an A record is enough. If you want direct IPv6 access as well, you need a working AAAA record and an IPv6 firewall that allows the connection. If IPv6 does not answer and you do not need it, switch it off for the subdomain: clients then go straight to IPv4 instead of waiting for a dead address.

Certificate (DNS-01)

Certificate (DNS-01)

If ports 80 or 443 are closed on your side, or you want one certificate for several names (a wildcard certificate), the certificate can be issued by validating a DNS record — your CA then only looks at a TXT record that we publish for you.

  1. Open your subdomain in the dashboard and turn on the wildcard switch if names such as www.my-home should answer as well.
  2. Run your ACME client so that it prints the challenge value:certbot certonly --manual --preferred-challenges dns -d my-home.bhdns.de
  3. Paste that value into the dashboard field Certificate (DNS-01). It appears as the TXT record _acme-challenge.my-home.bhdns.de.

The value is kept for 30 minutes and then removed automatically. If you are too late, run the client again.

Automatically, without the dashboard

Use the token your router already has — the answers are the same as for DynDNS updates:

curl -fsS "https://bhdns.de/nic/acme?hostname=my-home.bhdns.de&value=$CERTBOT_VALIDATION" -u "my-home.bhdns.de:<token>"

good = written, badauth = wrong token, nohost = the name does not belong to that token, invalid = the value is not valid, abuse = too many requests, try again later.

What is deliberately not possible

Apart from the _acme-challenge record you cannot publish other TXT records on your subdomain (for example a DKIM key) or delegate your own nameserver. This protects every user: otherwise it would be possible to sign forged mail with our domain and bypass the protection we put in place.

If something does not work

Check that the value is exactly 43 characters (that is what the client prints) and query both servers:

dig +short TXT _acme-challenge.my-home.bhdns.de @ns1.bhdns.dedig +short TXT _acme-challenge.my-home.bhdns.de @ns2.bhdns.de
AVM

AVM FRITZ!Box

FRITZ!OS has a built-in DynDNS client that can also send IPv6; whether it does depends on the model and the firmware. Menu names differ a little between versions; the field values do not.

  1. Open Internet → Shares → DynDNS (on some versions Internet → Permit Access → DynDNS).
  2. Tick Use dynamic DNS.
  3. Dynamic DNS provider: User-defined.
  4. Update URL— enter exactly this, including the placeholders:
    https://update.bhdns.de/nic/update?hostname=<domain>&myip=<ipaddr>&myipv6=<ip6addr>
  5. Domain name: my-home.bhdns.de
  6. Username: my-home.bhdns.de
  7. Password: your update token.

FRITZ!Box replaces <domain>, <ipaddr> and <ip6addr> itself, so both records stay current. If your firmware rejects the URL, remove the two address parameters and let the router send only hostname— the service then uses the address the request came from.

The FRITZ!Box has a single Update URL field, so here the scheme does belong in the URL. This is the opposite of routers whose form separates the address from the protocol and the port (see Speedportandother routers), where https:// in the address field is rejected.

Telekom

Telekom Speedport

Speedport models expose dynamic DNS under Internet → Dynamic DNS (naming varies by model and firmware). The form splits the destination into an address, a protocol and a port— and it rejects the address if you paste https:// into it. Enter the address bare and choose the protocol and the port in their own fields.

Provider / AnbieterUser-defined / Other / Anderer Anbieter
Name (labelled Hostname on some firmwares)my-home.bhdns.de— the whole name, not just my-home
Username / Benutzermy-home.bhdns.de— the same whole name
Passwortyour update token
Updateserver-Adresseupdate.bhdns.de/nic/update— no https://, no port, no trailing slash
ProtokollHTTPS — mandatory. Plain HTTP is only redirected, and the Speedport does not follow redirects.
Port443 — mandatory. Encrypted HTTPS port; anything else times out.

You do not have to guess which field carries the name. Whichever field the firmware sends it in — the query string, Name or Username— the update works. If none of them contains the whole name, the server still recognises the subdomain from the token alone, because a token belongs to exactly one subdomain. Enter the same full name in Name and Username and it is supported by the tested Speedport models. Field names and behaviour may still vary by model and firmware.

The status field may keep saying not registered until the first successful update has gone through — it is the router's own display, not our answer. If it stays that way, check the router's log: a valid request is answered with good <ip> or nochg <ip>, and badauth means the token is wrong.

Many Speedport versions do not offer IPv6 for dynamic DNS. In that case the A record is maintained by the router, and IPv6 can be updated by a script (below) or by ticking IPv6 in a newer firmware.

More

Other routers and NAS

Anything that speaks dyndns2 works — choose "user-defined" provider and use the values from thequick start.

Address, protocol and port are three separate fields on most routers. Into the address field goes update.bhdns.de/nic/update— bare, without https://, without a port and without a trailing slash. Then select HTTPS and port 443; the service answers on nothing else. A router that shows "not registered" while its log says the URL was refused has almost always had the scheme pasted into the address field.

  • OpenWrt: opkg install ddns-scripts luci-app-ddns, then Services → Dynamic DNS → provider custom, update URL https://update.bhdns.de/nic/update?hostname=[DOMAIN]&myip=[IP]&myipv6=[IP6], domain, username and token.
  • Synology DSM: Control Panel → External Access → DDNS → Add → Service provider Customize, with the same URL and credentials.
  • pfSense / OPNsense: Services → Dynamic DNS → Add, Service type Custom, interface (IPv4 and/or IPv6), URL with %IP%/%IP6% placeholders.
  • Raspberry Pi, servers, containers: use the scripts below with a cron job or a systemd timer.
Scripts

Scripts you can copy

Each script sends both addresses when it can read them. Replace the three values at the top with your own.

Linux / macOS (bash)

#!/bin/sh
HOST="my-home.bhdns.de"
TOKEN="bhdns_your_token"
URL="https://update.bhdns.de/nic/update"

IP4=$(curl -fsS https://v4.bhdns.de/nic/whoami || true)
[ -n "$IP4" ] || { echo "bhdns: the IPv4 address could not be detected — is this machine online?" >&2; exit 1; }
IP6=$(curl -fsS https://v6.bhdns.de/nic/whoami || true)

# Only the addresses that were actually found are sent. An empty parameter is ignored by the server, but leaving it out says what you mean.
QUERY="hostname=$HOST"
[ -n "$IP4" ] && QUERY="$QUERY&myip=$IP4"
[ -n "$IP6" ] && QUERY="$QUERY&myipv6=$IP6"

curl -fsS -u "$HOST:$TOKEN" "$URL?$QUERY"
echo

Cron (every five minutes)

*/5 * * * * /usr/local/bin/bhdns-update.sh >/dev/null 2>&1

systemd timer (better than cron)

Two files, not one: save each block under the path shown in its first line.

# /etc/systemd/system/bhdns-update.service
[Unit]
Description=Update bhdns.de dynamic DNS

[Service]
Type=oneshot
ExecStart=/usr/local/bin/bhdns-update.sh
# /etc/systemd/system/bhdns-update.timer
[Unit]
Description=Run bhdns.de update every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target

Then: systemctl enable --now bhdns-update.timer.

Check that the timer is scheduled and that a run has gone through: systemctl list-timers bhdns-update.timer journalctl -u bhdns-update.service

Windows (PowerShell)

$host_ = "my-home.bhdns.de"
$token = "bhdns_your_token"
$pair  = $host_ + ":" + $token
$auth  = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))

# IPv4 (required), IPv6 if your network supports it
$ip4 = (Invoke-RestMethod "https://v4.bhdns.de/nic/whoami").Trim()
# IPv6 is optional: if the lookup fails, the update is sent without myipv6 and the existing AAAA record stays as it is.
$ip6 = ""
try { $ip6 = (Invoke-RestMethod "https://v6.bhdns.de/nic/whoami").Trim() } catch {}

$url = "https://update.bhdns.de/nic/update?hostname=$host_&myip=$ip4"
if ($ip6) { $url += "&myipv6=$ip6" }
Invoke-RestMethod $url -Headers @{ Authorization = "Basic $auth" }

Docker / NAS one-liner

curl -fsS -u "my-home.bhdns.de:bhdns_your_token" \
  "https://update.bhdns.de/nic/update?hostname=my-home.bhdns.de"

Without myip the service uses the address the request came from, which is what you want for a one-liner.

Fair use

Limits of the free tier

We host subdomains under two domains: bhdns.de and sbdns.de— you pick one when you claim a name. Each domain allows 5 subdomains, so 10 in total across our 2 domains — adding another domain raises the total by 5. Bringing your own domain is not supported yet. The free tier allows 10 subdomains and 64 updates in 24 hours.

The 64 are counted as address changes, not as requests. A router may ask as often as it likes — that is how FRITZ!Box works — but the address may only be rewritten this many times:

LimitValue
Subdomains per account10
Subdomains per domain5
Address changes per host / 24 h64
Requests per host / 24 h2000
Requests per address / 24 h3000
Shortest gap between two changes30s
New subdomains per account / 24 h10

A two-letter name is reserved for a paid tier; free names have at least three characters. When a limit is hit the client receives abuse, and the reason is written to the log.

Names are also refused when they contain words used for abuse, piracy or adult material — including simple disguises such as p-o-r-n or p0rn. This applies to every account.

Automatic removal

Inactive subdomains

A subdomain is meant to answer with your address. One that has never sent a single check-in is not doing that: the name is taken, but nothing uses it. We remove such a subdomain automatically — 7 days after it was created — so that the name becomes free again.

You get a warning first. 24 hours before the removal we send an e-mail to the account's address. If nothing happens in those 24 hours, the subdomain and its address records are deleted.

Any check-in from your router, or any change to the subdomain in the dashboard (address, IPv6, TTL), cancels the removal. Using a static address without a dynamic DNS client? Save the address in the dashboard once — that also counts.

Subdomains that ever worked are never removed. A subdomain that has sent at least one check-in stays yours, even if your router is offline for weeks — a holiday, an internet outage or a broken router does not cost you the name.

Abuse

Abuse reports

If a subdomain is reported for abuse — spam, phishing, malware or anything else illegal — that single name is suspended. Its records are withdrawn from the authoritative nameservers immediately, and you cannot activate it yourself; only an administrator can lift the suspension. Cached answers at resolvers may remain until their TTL expires. The rest of your account keeps working.

A suspension is not a verdict: if the report was wrong, get in touch (see the imprintfor the address) and it will be reviewed and lifted. Suspensions are never deleted automatically, and every one is recorded with the report it is based on.

You can always delete the subdomain yourself, suspension or not — that removes the name from DNS at once and frees it.

To report a subdomain used for phishing, malware or spam, use the abuse form. Every report is recorded, an administrator is notified and the subdomain can be suspended immediately.

We do not send e-mail about suspensions, bans or administrator actions. Every such change appears in the notification centre— the bell in the header of every page. If your account is suspended you cannot sign in, so the sign-in page tells you what happened and where to get in touch; the notice is waiting in the centre as soon as the suspension is lifted.

Questions

Questions

How fast does a change appear?

Immediately on this server. The secondary nameserver picks it up on the next zone transfer, and resolvers may cache the old answer until the 60-second TTL expires. Until the secondary has the zone, a resolver that asks it can still get the old address — which is why the two serials on the status page have to match.

Why was my e-mail refused?

Disposable inbox services (10minutemail, mailinator, yopmail and similar) are not accepted: the address disappears within minutes, so the account could never be recovered. Use an address you actually keep.

I lost my token

Open the subdomain in your dashboard and issue a new one. The old token stops working at that moment, so update the router afterwards.

Does the username have to be the full hostname?

No. The update token (the password) identifies the subdomain, so the username may be the full name, just the label, or any text your router accepts — a wrong name in that field cannot reach someone else's subdomain. Handy if your router limits what you can type there (some Speedport models do); leaving the field empty works too.

My router refuses the password

Some routers limit how long the password field may be — the FRITZ!Box accepts 32 characters, the Speedport Smart 4 no more than 50. Tokens issued by this service are 30 characters, so they fit. If you still have an older, longer token from before that change, issue a new one in the dashboard.

My address is behind NAT

The service stores the address you send, as long as it is a public one; reserved and private ranges are refused (see the question above). Behind carrier-grade NAT the address you send is your provider's — reachability then depends on your provider, not on DNS.

Can I point the name at a private address?

No. Private, loopback and other reserved ranges (for example 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 100.64.0.0/10) are refused with the answer 911 and the log names the range. Public DNS could technically return a private address, but that address is not directly reachable from the public internet — which is why such a record would be useless for a router or a NAS. For a LAN or VPN setup, publish your public address and resolve the name locally instead (local DNS or a hosts entry).

Is there an API for my own tooling?

Theupdate protocolis the API: one GET with Basic auth. Any client that speaks dyndns2 works unchanged.

Something unclear or a router that refuses to work? The logs in your dashboard show every attempt with its answer, which is the fastest way to see what the router sent.