Repository: go-acme/lego
Pull request #2540 (2025-06-19 08:20:20)

Add DNS provider for DynDnsFree.de
Adds a DNS provider for [DynDnsFree.de](https://www.dyndnsfree.de)


ldez: Hello, in order for a PR adding a DNS provider to be accepted, you have to:

- [ ] add a description to your PR
- [ ] public API documentation
- [ ] be able to maintain this provider
- [ ] have a homogeneous design with the other providers
- [ ] add tests (units)
```shell
make test
```
- [ ] add tests ("live")
https://github.com/go-acme/lego/blob/c938de68f262c057542d44bcb67ef961cefcc60e/providers/dns/cloudflare/cloudflare_test.go#L125-L151

```shell
make test
```
- [ ] add a [provider descriptor](https://github.com/go-acme/lego/blob/master/providers/dns/cloudflare/cloudflare.toml)
- [ ] generate CLI help, documentation, and readme.
```bash
make generate-dns
```
- [ ] be able to do: _(and put the output of this command to a comment in your PR)_
```bash
rm -rf .lego

./lego -m your@email.com --dns YOUR_PROVIDER_NAME -d *.example.com -d example.com -s https://acme-staging-v02.api.letsencrypt.org/directory run
```
Note the wildcard domain is important.
- [ ] pass the linter ([golangci-lint](https://github.com/golangci/golangci-lint#install) must be installed):
```shell
make checks
```
- [ ] do `go mod tidy`
joestr: - public API documentation
  The API is not publicly documented. It is in the customer panel.
  ![image](https://github.com/user-attachments/assets/514411f8-780a-47d8-9bb6-d8d35e60e876)

  The TTL and life time of the TXT record are set to one hour.
- be able to maintain this provider
  Yep. I can do that.
- be able to do: (and put the output of this command to a comment in your PR)
  ```
  joestr@joel-vm3:~/Development/git/github.com/joestr/lego-dyndnsforfree/lego-dyndnsforfree$ export DYNDNSFREE_USERNAME="REDACTED"; export DYNDNSFREE_PASSWORD="REDACTED"; ./dist/lego -m REDACTED --dns dyndnsfree -d *.REDACTED -d REDACTED -s https://acme-staging-v02.api.letsencrypt.org/directory run
  2025/06/19 11:43:09 No key found for account REDACTED. Generating a P256 key.
  2025/06/19 11:43:09 Saved key to /home/joestr/Development/git/github.com/joestr/lego-dyndnsforfree/lego-dyndnsforfree/.lego/accounts/acme-staging-v02.api.letsencrypt.org/REDACTED/keys/REDACTED.key
  2025/06/19 11:43:10 Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf
  Do you accept the TOS? Y/n
  y
  2025/06/19 11:43:14 [INFO] acme: Registering account for REDACTED
  !!!! HEADS UP !!!!
  
  Your account credentials have been saved in your
  configuration directory at "/home/joestr/Development/git/github.com/joestr/lego-dyndnsforfree/lego-dyndnsforfree/.lego/accounts".
  
  You should make a secure backup of this folder now. This
  configuration directory will also contain certificates and
  private keys obtained from the ACME server so making regular
  backups of this folder is ideal.
  2025/06/19 11:43:14 [INFO] [*.REDACTED, REDACTED] acme: Obtaining bundled SAN certificate
  2025/06/19 11:43:15 [INFO] [*.REDACTED] AuthURL: https://acme-staging-v02.api.letsencrypt.org/acme/authz/206984114/18108076104
  2025/06/19 11:43:15 [INFO] [REDACTED] AuthURL: https://acme-staging-v02.api.letsencrypt.org/acme/authz/206984114/18108076114
  2025/06/19 11:43:15 [INFO] [*.REDACTED] acme: use dns-01 solver
  2025/06/19 11:43:15 [INFO] [REDACTED] acme: Could not find solver for: tls-alpn-01
  2025/06/19 11:43:15 [INFO] [REDACTED] acme: Could not find solver for: http-01
  2025/06/19 11:43:15 [INFO] [REDACTED] acme: use dns-01 solver
  2025/06/19 11:43:15 [INFO] [*.REDACTED] acme: Preparing to solve DNS-01
  2025/06/19 11:43:16 [INFO] [REDACTED] acme: Preparing to solve DNS-01
  2025/06/19 11:43:16 [INFO] [*.REDACTED] acme: Trying to solve DNS-01
  2025/06/19 11:43:16 [INFO] [*.REDACTED] acme: Checking DNS record propagation. [nameservers=127.0.0.53:53]
  2025/06/19 11:43:18 [INFO] Wait for propagation [timeout: 1m0s, interval: 2s]
  2025/06/19 11:43:18 [INFO] [*.REDACTED] acme: Waiting for DNS record propagation.
  2025/06/19 11:43:20 [INFO] [*.REDACTED] acme: Waiting for DNS record propagation.
  2025/06/19 11:43:23 [INFO] [*.REDACTED] acme: Waiting for DNS record propagation.
  2025/06/19 11:43:25 [INFO] [*.REDACTED] acme: Waiting for DNS record propagation.
  2025/06/19 11:43:27 [INFO] [*.REDACTED] acme: Waiting for DNS record propagation.
  2025/06/19 11:43:36 [INFO] [*.REDACTED] The server validated our request
  2025/06/19 11:43:36 [INFO] [REDACTED] acme: Trying to solve DNS-01
  2025/06/19 11:43:36 [INFO] [REDACTED] acme: Checking DNS record propagation. [nameservers=127.0.0.53:53]
  2025/06/19 11:43:38 [INFO] Wait for propagation [timeout: 1m0s, interval: 2s]
  2025/06/19 11:43:45 [INFO] [REDACTED] The server validated our request
  2025/06/19 11:43:45 [INFO] [*.REDACTED] acme: Cleaning DNS-01 challenge
  2025/06/19 11:43:45 [INFO] [REDACTED] acme: Cleaning DNS-01 challenge
  2025/06/19 11:43:45 [INFO] [*.REDACTED, REDACTED] acme: Validations succeeded; requesting certificates
  2025/06/19 11:43:46 [INFO] Wait for certificate [timeout: 30s, interval: 500ms]
  2025/06/19 11:43:48 [INFO] [*.REDACTED] Server responded with a certificate.
  joestr@joel-vm3:~/Development/git/github.com/joestr/lego-dyndnsforfree/lego-dyndnsforfree$ 
  ```
joestr: It was no AI slop that's for sure. It was an attempt to get it just working. I looked down how the active24 client was implemented but was unsure whether where to go from there and just called the URL with the parameters i had at hand.

The program did compile at first but I committed to a later point which had some cruft left in. Sorry.

Thank you for the clean up!

Patch:
diff --git a/README.md b/README.md
index c4d4fd9c..90e60436 100644
--- a/README.md
+++ b/README.md
@@ -108,145 +108,145 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
   <td><a href="https://go-acme.github.io/lego/dns/duckdns/">Duck DNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/dyn/">Dyn</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/dyndnsfree/">DynDnsFree.de</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/dynu/">Dynu</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/easydns/">EasyDNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/efficientip/">Efficient IP</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/epik/">Epik</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/epik/">Epik</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/exoscale/">Exoscale</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/exec/">External program</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/f5xc/">F5 XC</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/freemyip/">freemyip.com</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/freemyip/">freemyip.com</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/gcore/">G-Core</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/gandi/">Gandi</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/gandiv5/">Gandi Live DNS (v5)</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/glesys/">Glesys</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/glesys/">Glesys</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/godaddy/">Go Daddy</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/gcloud/">Google Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/googledomains/">Google Domains</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/hetzner/">Hetzner</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/hetzner/">Hetzner</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/hostingde/">Hosting.de</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/hosttech/">Hosttech</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/httpreq/">HTTP request</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/httpnet/">http.net</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/httpnet/">http.net</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/huaweicloud/">Huawei Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/hurricane/">Hurricane Electric DNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/hyperone/">HyperOne</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/ibmcloud/">IBM Cloud (SoftLayer)</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/ibmcloud/">IBM Cloud (SoftLayer)</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/iijdpf/">IIJ DNS Platform Service</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/infoblox/">Infoblox</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/infomaniak/">Infomaniak</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/iij/">Internet Initiative Japan</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/iij/">Internet Initiative Japan</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/internetbs/">Internet.bs</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/inwx/">INWX</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/ionos/">Ionos</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/ipv64/">IPv64</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/ipv64/">IPv64</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/iwantmyname/">iwantmyname</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/joker/">Joker</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/acme-dns/">Joohoi&#39;s ACME-DNS</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/liara/">Liara</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/liara/">Liara</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/limacity/">Lima-City</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/linode/">Linode (v4)</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/liquidweb/">Liquid Web</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/loopia/">Loopia</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/loopia/">Loopia</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/luadns/">LuaDNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/mailinabox/">Mail-in-a-Box</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/manageengine/">ManageEngine CloudDNS</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/manual/">Manual</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/manual/">Manual</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/metaname/">Metaname</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/metaregistrar/">Metaregistrar</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/mijnhost/">mijn.host</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/mittwald/">Mittwald</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/mittwald/">Mittwald</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/myaddr/">myaddr.{tools,dev,io}</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/mydnsjp/">MyDNS.jp</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/mythicbeasts/">MythicBeasts</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/namedotcom/">Name.com</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/namedotcom/">Name.com</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/namecheap/">Namecheap</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/namesilo/">Namesilo</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/nearlyfreespeech/">NearlyFreeSpeech.NET</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/netcup/">Netcup</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/netcup/">Netcup</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/netlify/">Netlify</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/nicmanager/">Nicmanager</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/nifcloud/">NIFCloud</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/njalla/">Njalla</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/njalla/">Njalla</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/nodion/">Nodion</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/ns1/">NS1</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/otc/">Open Telekom Cloud</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/oraclecloud/">Oracle Cloud</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/oraclecloud/">Oracle Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/ovh/">OVH</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/plesk/">plesk.com</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/porkbun/">Porkbun</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/pdns/">PowerDNS</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/pdns/">PowerDNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/rackspace/">Rackspace</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/rainyun/">Rain Yun/雨云</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/rcodezero/">RcodeZero</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/regru/">reg.ru</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/regru/">reg.ru</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/regfish/">Regfish</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/rfc2136/">RFC2136</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/rimuhosting/">RimuHosting</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/nicru/">RU CENTER</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/nicru/">RU CENTER</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/sakuracloud/">Sakura Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/scaleway/">Scaleway</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/selectel/">Selectel</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/selectelv2/">Selectel v2</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/selectelv2/">Selectel v2</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/selfhostde/">SelfHost.(de|eu)</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/servercow/">Servercow</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/shellrent/">Shellrent</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/simply/">Simply.com</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/simply/">Simply.com</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/sonic/">Sonic</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/spaceship/">Spaceship</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/stackpath/">Stackpath</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/technitium/">Technitium</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/technitium/">Technitium</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/tencentcloud/">Tencent Cloud DNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/timewebcloud/">Timeweb Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/transip/">TransIP</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/safedns/">UKFast SafeDNS</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/safedns/">UKFast SafeDNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/ultradns/">Ultradns</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/variomedia/">Variomedia</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/vegadns/">VegaDNS</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/vercel/">Vercel</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/vercel/">Vercel</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/versio/">Versio.[nl|eu|uk]</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/vinyldns/">VinylDNS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/vkcloud/">VK Cloud</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/volcengine/">Volcano Engine/火山引擎</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/volcengine/">Volcano Engine/火山引擎</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/vscale/">Vscale</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/vultr/">Vultr</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/webnames/">Webnames</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/websupport/">Websupport</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/websupport/">Websupport</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/wedos/">WEDOS</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/westcn/">West.cn/西部数码</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/yandex360/">Yandex 360</a></td>
-  <td><a href="https://go-acme.github.io/lego/dns/yandexcloud/">Yandex Cloud</a></td>
 </tr><tr>
+  <td><a href="https://go-acme.github.io/lego/dns/yandexcloud/">Yandex Cloud</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/yandex/">Yandex PDD</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/zoneee/">Zone.ee</a></td>
   <td><a href="https://go-acme.github.io/lego/dns/zonomi/">Zonomi</a></td>
-  <td></td>
 </tr></table>
 
 <!-- END DNS PROVIDERS LIST -->
diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go
index c160bd82..ee6f726c 100644
--- a/cmd/zz_gen_cmd_dnshelp.go
+++ b/cmd/zz_gen_cmd_dnshelp.go
@@ -55,6 +55,7 @@ func allDNSCodes() string {
 		"dreamhost",
 		"duckdns",
 		"dyn",
+		"dyndnsfree",
 		"dynu",
 		"easydns",
 		"edgedns",
@@ -1096,6 +1097,26 @@ func displayDNSHelp(w io.Writer, name string) error {
 		ew.writeln()
 		ew.writeln(`More information: https://go-acme.github.io/lego/dns/dyn`)
 
+	case "dyndnsfree":
+		// generated from: providers/dns/dyndnsfree/dyndnsfree.toml
+		ew.writeln(`Configuration for DynDnsFree.de.`)
+		ew.writeln(`Code:	'dyndnsfree'`)
+		ew.writeln(`Since:	'v4.23.0'`)
+		ew.writeln()
+
+		ew.writeln(`Credentials:`)
+		ew.writeln(`	- "DYNDNSFREE_PASSWORD":	Password`)
+		ew.writeln(`	- "DYNDNSFREE_USERNAME":	Username`)
+		ew.writeln()
+
+		ew.writeln(`Additional Configuration:`)
+		ew.writeln(`	- "DYNDNSFREE_HTTP_TIMEOUT":	Request timeout in seconds (Default: 30)`)
+		ew.writeln(`	- "DYNDNSFREE_POLLING_INTERVAL":	Time between DNS propagation check in seconds (Default: 2)`)
+		ew.writeln(`	- "DYNDNSFREE_PROPAGATION_TIMEOUT":	Maximum waiting time for DNS propagation in seconds (Default: 60)`)
+
+		ew.writeln()
+		ew.writeln(`More information: https://go-acme.github.io/lego/dns/dyndnsfree`)
+
 	case "dynu":
 		// generated from: providers/dns/dynu/dynu.toml
 		ew.writeln(`Configuration for Dynu.`)
diff --git a/docs/content/dns/zz_gen_dyndnsfree.md b/docs/content/dns/zz_gen_dyndnsfree.md
new file mode 100644
index 00000000..6f4cf46f
--- /dev/null
+++ b/docs/content/dns/zz_gen_dyndnsfree.md
@@ -0,0 +1,68 @@
+---
+title: "DynDnsFree.de"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dyndnsfree
+dnsprovider:
+  since:    "v4.23.0"
+  code:     "dyndnsfree"
+  url:      "https://www.dyndnsfree.de"
+---
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/dyndnsfree/dyndnsfree.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+
+
+Configuration for [DynDnsFree.de](https://www.dyndnsfree.de).
+
+
+<!--more-->
+
+- Code: `dyndnsfree`
+- Since: v4.23.0
+
+
+Here is an example bash command using the DynDnsFree.de provider:
+
+```bash
+DYNDNSFREE_USERNAME="xxx" \
+DYNDNSFREE_PASSWORD="yyy" \
+lego --email you@example.com --dns dyndnsfree -d '*.example.com' -d example.com run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DYNDNSFREE_PASSWORD` | Password |
+| `DYNDNSFREE_USERNAME` | Username |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here]({{% ref "dns#configuration-and-credentials" %}}).
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DYNDNSFREE_HTTP_TIMEOUT` | Request timeout in seconds (Default: 30) |
+| `DYNDNSFREE_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
+| `DYNDNSFREE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here]({{% ref "dns#configuration-and-credentials" %}}).
+
+
+
+
+## More information
+
+- [API documentation](https://www.dyndnsfree.de/user/hilfe.php?hsm=2)
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/dyndnsfree/dyndnsfree.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml
index f06353e4..8fcb47ba 100644
--- a/docs/data/zz_cli_help.toml
+++ b/docs/data/zz_cli_help.toml
@@ -150,7 +150,7 @@ To display the documentation for a specific DNS provider, run:
   $ lego dnshelp -c code
 
 Supported DNS providers:
-  acme-dns, active24, alidns, allinkl, arvancloud, auroradns, autodns, axelname, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneee, zonomi
+  acme-dns, active24, alidns, allinkl, arvancloud, auroradns, autodns, axelname, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneee, zonomi
 
 More information: https://go-acme.github.io/lego/dns
 """
diff --git a/providers/dns/dyndnsfree/dyndnsfree.go b/providers/dns/dyndnsfree/dyndnsfree.go
new file mode 100644
index 00000000..8c1d87aa
--- /dev/null
+++ b/providers/dns/dyndnsfree/dyndnsfree.go
@@ -0,0 +1,118 @@
+// Package dyndnsfree implements a DNS provider for solving the DNS-01 challenge using DynDnsFree.de API.
+package dyndnsfree
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/go-acme/lego/v4/challenge/dns01"
+	"github.com/go-acme/lego/v4/platform/config/env"
+	"github.com/go-acme/lego/v4/providers/dns/dyndnsfree/internal"
+)
+
+// Environment variables names.
+const (
+	envNamespace = "DYNDNSFREE_"
+
+	EnvUsername = envNamespace + "USERNAME"
+	EnvPassword = envNamespace + "PASSWORD"
+
+	EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
+	EnvPollingInterval    = envNamespace + "POLLING_INTERVAL"
+	EnvHTTPTimeout        = envNamespace + "HTTP_TIMEOUT"
+)
+
+// Config is used to configure the creation of the DNSProvider.
+type Config struct {
+	Username string
+	Password string
+
+	PropagationTimeout time.Duration
+	PollingInterval    time.Duration
+	HTTPClient         *http.Client
+}
+
+// NewDefaultConfig returns a default configuration for the DNSProvider.
+func NewDefaultConfig() *Config {
+	return &Config{
+		PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
+		PollingInterval:    env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
+		HTTPClient: &http.Client{
+			Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
+		},
+	}
+}
+
+// DNSProvider implements the challenge.Provider interface.
+type DNSProvider struct {
+	config *Config
+	client *internal.Client
+}
+
+// NewDNSProvider returns a DNSProvider instance configured for DynDNSFree.
+func NewDNSProvider() (*DNSProvider, error) {
+	values, err := env.Get(EnvUsername, EnvPassword)
+	if err != nil {
+		return nil, fmt.Errorf("dyndnsfree: %w", err)
+	}
+
+	config := NewDefaultConfig()
+	config.Username = values[EnvUsername]
+	config.Password = values[EnvPassword]
+
+	return NewDNSProviderConfig(config)
+}
+
+// NewDNSProviderConfig return a DNSProvider instance configured for DynDNSFree.
+func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
+	if config == nil {
+		return nil, errors.New("dyndnsfree: the configuration of the DNS provider is nil")
+	}
+
+	client, err := internal.NewClient(config.Username, config.Password)
+	if err != nil {
+		return nil, fmt.Errorf("dyndnsfree: new client: %w", err)
+	}
+
+	if config.HTTPClient != nil {
+		client.HTTPClient = config.HTTPClient
+	}
+
+	return &DNSProvider{
+		config: config,
+		client: client,
+	}, nil
+}
+
+// Present creates a TXT record using the specified parameters.
+func (d *DNSProvider) Present(domain, token, keyAuth string) error {
+	info := dns01.GetChallengeInfo(domain, keyAuth)
+
+	authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
+	if err != nil {
+		return fmt.Errorf("dyndnsforfree: could not find zone for domain %q: %w", domain, err)
+	}
+
+	err = d.client.AddTXTRecord(context.Background(), dns01.UnFqdn(authZone), dns01.UnFqdn(info.EffectiveFQDN), info.Value)
+	if err != nil {
+		return fmt.Errorf("dyndnsfree: add record: %w", err)
+	}
+
+	return nil
+}
+
+// CleanUp removes the TXT record matching the specified parameters.
+func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
+	// Records are deleted automatically.
+
+	return nil
+}
+
+// Timeout returns the timeout and interval to use when checking for DNS propagation.
+// Adjusting here to cope with spikes in propagation times.
+func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
+	return d.config.PropagationTimeout, d.config.PollingInterval
+}
diff --git a/providers/dns/dyndnsfree/dyndnsfree.toml b/providers/dns/dyndnsfree/dyndnsfree.toml
new file mode 100644
index 00000000..dd354fb3
--- /dev/null
+++ b/providers/dns/dyndnsfree/dyndnsfree.toml
@@ -0,0 +1,23 @@
+Name = "DynDnsFree.de"
+Description = ''''''
+URL = "https://www.dyndnsfree.de"
+Code = "dyndnsfree"
+Since = "v4.23.0"
+
+Example = '''
+DYNDNSFREE_USERNAME="xxx" \
+DYNDNSFREE_PASSWORD="yyy" \
+lego --email you@example.com --dns dyndnsfree -d '*.example.com' -d example.com run
+'''
+
+[Configuration]
+  [Configuration.Credentials]
+    DYNDNSFREE_USERNAME = "Username"
+    DYNDNSFREE_PASSWORD = "Password"
+  [Configuration.Additional]
+    DYNDNSFREE_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
+    DYNDNSFREE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
+    DYNDNSFREE_HTTP_TIMEOUT = "Request timeout in seconds (Default: 30)"
+
+[Links]
+  API = "https://www.dyndnsfree.de/user/hilfe.php?hsm=2"
diff --git a/providers/dns/dyndnsfree/internal/client.go b/providers/dns/dyndnsfree/internal/client.go
new file mode 100644
index 00000000..02a1f160
--- /dev/null
+++ b/providers/dns/dyndnsfree/internal/client.go
@@ -0,0 +1,78 @@
+package internal
+
+import (
+	"bytes"
+	"context"
+	"errors"
+	"io"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
+)
+
+const defaultBaseURL = "https://dynup.de/acme.php"
+
+type Client struct {
+	username string
+	password string
+
+	baseURL    string
+	HTTPClient *http.Client
+}
+
+func NewClient(username, password string) (*Client, error) {
+	if username == "" || password == "" {
+		return nil, errors.New("credentials missing")
+	}
+
+	return &Client{
+		username:   username,
+		password:   password,
+		baseURL:    defaultBaseURL,
+		HTTPClient: &http.Client{Timeout: 10 * time.Second},
+	}, nil
+}
+
+func (c *Client) AddTXTRecord(ctx context.Context, zone, hostname, value string) error {
+	baseURL, err := url.Parse(c.baseURL)
+	if err != nil {
+		return err
+	}
+
+	query := baseURL.Query()
+	query.Set("username", c.username)
+	query.Set("password", c.password)
+	query.Set("hostname", zone)
+	query.Set("add_hostname", hostname)
+	query.Set("txt", value)
+	baseURL.RawQuery = query.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL.String(), http.NoBody)
+	if err != nil {
+		return err
+	}
+
+	resp, err := c.HTTPClient.Do(req)
+	if err != nil {
+		return errutils.NewHTTPDoError(req, err)
+	}
+
+	defer func() { _ = resp.Body.Close() }()
+
+	if resp.StatusCode != http.StatusOK {
+		return errutils.NewUnexpectedResponseStatusCodeError(req, resp)
+	}
+
+	raw, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return errutils.NewReadResponseError(req, resp.StatusCode, err)
+	}
+
+	if !bytes.Equal(raw, []byte("success")) {
+		return errors.New(string(raw))
+	}
+
+	return nil
+}
diff --git a/providers/dns/zz_gen_dns_providers.go b/providers/dns/zz_gen_dns_providers.go
index eba28d83..ef9dcba2 100644
--- a/providers/dns/zz_gen_dns_providers.go
+++ b/providers/dns/zz_gen_dns_providers.go
@@ -49,6 +49,7 @@ import (
 	"github.com/go-acme/lego/v4/providers/dns/dreamhost"
 	"github.com/go-acme/lego/v4/providers/dns/duckdns"
 	"github.com/go-acme/lego/v4/providers/dns/dyn"
+	"github.com/go-acme/lego/v4/providers/dns/dyndnsfree"
 	"github.com/go-acme/lego/v4/providers/dns/dynu"
 	"github.com/go-acme/lego/v4/providers/dns/easydns"
 	"github.com/go-acme/lego/v4/providers/dns/edgedns"
@@ -252,6 +253,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
 		return duckdns.NewDNSProvider()
 	case "dyn":
 		return dyn.NewDNSProvider()
+	case "dyndnsfree":
+		return dyndnsfree.NewDNSProvider()
 	case "dynu":
 		return dynu.NewDNSProvider()
 	case "easydns":


Test patch:
diff --git a/providers/dns/dyndnsfree/dyndnsfree_test.go b/providers/dns/dyndnsfree/dyndnsfree_test.go
new file mode 100644
index 00000000..cb063a02
--- /dev/null
+++ b/providers/dns/dyndnsfree/dyndnsfree_test.go
@@ -0,0 +1,143 @@
+package dyndnsfree
+
+import (
+	"testing"
+
+	"github.com/go-acme/lego/v4/platform/tester"
+	"github.com/stretchr/testify/require"
+)
+
+const envDomain = envNamespace + "DOMAIN"
+
+var envTest = tester.NewEnvTest(EnvUsername, EnvPassword).WithDomain(envDomain)
+
+func TestNewDNSProvider(t *testing.T) {
+	testCases := []struct {
+		desc     string
+		envVars  map[string]string
+		expected string
+	}{
+		{
+			desc: "success",
+			envVars: map[string]string{
+				EnvUsername: "user",
+				EnvPassword: "secret",
+			},
+		},
+		{
+			desc: "missing username",
+			envVars: map[string]string{
+				EnvUsername: "",
+				EnvPassword: "secret",
+			},
+			expected: "dyndnsfree: some credentials information are missing: DYNDNSFREE_USERNAME",
+		},
+		{
+			desc: "missing password",
+			envVars: map[string]string{
+				EnvUsername: "user",
+				EnvPassword: "",
+			},
+			expected: "dyndnsfree: some credentials information are missing: DYNDNSFREE_PASSWORD",
+		},
+		{
+			desc:     "missing credentials",
+			envVars:  map[string]string{},
+			expected: "dyndnsfree: some credentials information are missing: DYNDNSFREE_USERNAME,DYNDNSFREE_PASSWORD",
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.desc, func(t *testing.T) {
+			defer envTest.RestoreEnv()
+			envTest.ClearEnv()
+
+			envTest.Apply(test.envVars)
+
+			p, err := NewDNSProvider()
+
+			if test.expected == "" {
+				require.NoError(t, err)
+				require.NotNil(t, p)
+				require.NotNil(t, p.config)
+			} else {
+				require.EqualError(t, err, test.expected)
+			}
+		})
+	}
+}
+
+func TestNewDNSProviderConfig(t *testing.T) {
+	testCases := []struct {
+		desc     string
+		username string
+		password string
+		expected string
+	}{
+		{
+			desc:     "success",
+			username: "user",
+			password: "secret",
+		},
+		{
+			desc:     "missing username",
+			username: "",
+			password: "secret",
+			expected: "dyndnsfree: new client: credentials missing",
+		},
+		{
+			desc:     "missing password",
+			username: "user",
+			password: "",
+			expected: "dyndnsfree: new client: credentials missing",
+		},
+		{
+			desc:     "missing credentials",
+			expected: "dyndnsfree: new client: credentials missing",
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.desc, func(t *testing.T) {
+			config := NewDefaultConfig()
+			config.Username = test.username
+			config.Password = test.password
+
+			p, err := NewDNSProviderConfig(config)
+
+			if test.expected == "" {
+				require.NoError(t, err)
+				require.NotNil(t, p)
+				require.NotNil(t, p.config)
+			} else {
+				require.EqualError(t, err, test.expected)
+			}
+		})
+	}
+}
+
+func TestLivePresent(t *testing.T) {
+	if !envTest.IsLiveTest() {
+		t.Skip("skipping live test")
+	}
+
+	envTest.RestoreEnv()
+	provider, err := NewDNSProvider()
+	require.NoError(t, err)
+
+	err = provider.Present(envTest.GetDomain(), "", "123d==")
+	require.NoError(t, err)
+}
+
+func TestLiveCleanUp(t *testing.T) {
+	if !envTest.IsLiveTest() {
+		t.Skip("skipping live test")
+	}
+
+	envTest.RestoreEnv()
+	provider, err := NewDNSProvider()
+	require.NoError(t, err)
+
+	err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
+	require.NoError(t, err)
+}
diff --git a/providers/dns/dyndnsfree/internal/client_test.go b/providers/dns/dyndnsfree/internal/client_test.go
new file mode 100644
index 00000000..17ac89b0
--- /dev/null
+++ b/providers/dns/dyndnsfree/internal/client_test.go
@@ -0,0 +1,58 @@
+package internal
+
+import (
+	"context"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func setupTest(t *testing.T, message string) *Client {
+	t.Helper()
+
+	mux := http.NewServeMux()
+	server := httptest.NewServer(mux)
+	t.Cleanup(server.Close)
+
+	client, err := NewClient("user", "secret")
+	require.NoError(t, err)
+
+	client.HTTPClient = server.Client()
+	client.baseURL = server.URL
+
+	mux.HandleFunc("GET /", func(rw http.ResponseWriter, req *http.Request) {
+		query := req.URL.Query()
+
+		username := query.Get("username")
+		if username != "user" {
+			http.Error(rw, "invalid username: "+username, http.StatusUnauthorized)
+			return
+		}
+
+		password := query.Get("password")
+		if password != "secret" {
+			http.Error(rw, "invalid password: "+password, http.StatusUnauthorized)
+			return
+		}
+
+		_, _ = rw.Write([]byte(message))
+	})
+
+	return client
+}
+
+func TestAddTXTRecord(t *testing.T) {
+	client := setupTest(t, "success")
+
+	err := client.AddTXTRecord(context.Background(), "example.com", "sub.example.com", "value")
+	require.NoError(t, err)
+}
+
+func TestAddTXTRecord_error(t *testing.T) {
+	client := setupTest(t, "error: authentification failed")
+
+	err := client.AddTXTRecord(context.Background(), "example.com", "sub.example.com", "value")
+	require.EqualError(t, err, "error: authentification failed")
+}