user36027: Imagine a web page that wants to accept Bitcoin donations. A naive approach is to provide a single address on the page and that's it. The problem here is that the address is known to everyone and if the money ever moves, the address is being reused for new donations after the first spend.
OK, so how can we do better? We can have xpub and generate a new address every time ... every time what? The first basic strategy would be to generate a new address as soon as someone sends a donation to the previously generated one. The problem here is that an active monitoring of the web page by a robot simply reveals all the addresses again.
Can we do better? We can, for example, generate a new address per IP address. Doing this, the robot would always see one address that is dedicated to its IP address and unless someone from the robot's network under the same IP address donated, the robot could not reveal anything useful. The problem here is that if we assume not very high conversion rate, we generate a lot of addresses and most of them never receive any donation. This causes a problem with seed recovery, where there usually is a gap limit defined for the maximum number of empty addresses that are scanned before the scan is terminated. I assume this is quite wallet specific rule, but I assume in general, the problem exists here (does it really?).
So how can we do better? Say we want to use a cold storage using a 24 word seed + passphrase and we are happy to run Bitcoin Core together with the web page. How can we solve donation in a way that is manageable, can be recovered from seed, and at the same time optimizes for privacy? Does a solution already exists somewhere (for example BTCPayServer)? If so, how does it work and what are the privacy properties of such a solution?

user-1: 
The first basic strategy would be to generate a new address as soon as someone sends a donation to the previously generated one.

This is not a good strategy. Consider Alice opens the donation page, looked at the address and decided to send nothing. Bob will also see the same address and send some bitcoin. Alice can track this donation or at least know some information about the inputs and history related to transaction.

Can we do better? We can, for example, generate a new address per IP address.

Few tools are available to use different IP on each request. So, this may not be the best approach.

The problem here is that if we assume not very high conversion rate, we generate a lot of addresses and most of them never receive any donation. This causes a problem with seed recovery, where there usually is a gap limit defined for the maximum number of empty addresses that are scanned before the scan is terminated. I assume this is quite wallet specific rule, but I assume in general, the problem exists here

Yes, according to my understanding 'Gap Limit' is an issue. The biggest issue in trying to solve this problem of privacy in donation addresses.

Does a solution already exists somewhere (for example BTCPayServer)? If so, how does it work and what are the privacy properties of such a solution?

BTCPayServer creates a new bitcoin address every time you open the donation page.
Example: https://btcpayjungle.com/api/v1/invoices?storeId=FSt2N7kxe7m5Nzbu5xxqSB83faZQG44Etx2wp266C4rC&price=10&currency=USD
I had created a new bitcoin wallet in electrum which has default gap limit of 20. I could find all the 20 addresses in my wallet by opening the above link 20 times. 21st request to open the page returned a new address: bc1qpzt7hax5cade0yu4grqn4aat62hdq9jj9cxuav which I could not see in my wallet.
I had to increase the gap limit by running the below command, relaunch electrum to see the address:
wallet.change_gap_limit(21)


So BTCPayserver is good for privacy in this case but will have issues managing wallet with huge gap limit if someone tries to attack.
Joinmarket donation page also provides a new bitcoin address every time you refresh the page: https://bitcoinprivacy.me/joinmarket-donations. Chris Belcher shared on twitter that lot of addresses are imported in a watch-only wallet(Core).
Other solutions:
Reusable Payment Codes: https://support.samourai.io/article/68-what-are-paynyms

This is an interesting problem to solve and I will update the answer if I find better solution or a way to use Bitcoin Core for donations which improves privacy and no other major issues.

user123812: Great question!
For each anonymous visitor who clicked [Donate]:

generate a new, high-entropy URL that is public to access but impossible to guess (additionally, it can expire after some time)
derive a new Bitcoin address based on xpub and the counter; this address is dedicated to this specific donation attempt

Even if anonymous visitor abandons the process without paying, her unique address is never reused. It is "wasted" in terms of the address space gap.
Other anonymous visitors will not be able to discover any past or future addresses because they can't guess the URL-s.
The only drawback is theoretical risk of pointless vandalism: one can click [Donate] in a loop, increasing the number of unused addresses in your wallet. This isn't really a big issue because most wallets can be configured to arbitrary gap. I also never heard of such vandalism attempts happening in practice.
Now, the most pragmatic way to go aobut it is use existing open source, self-hosted donations processors like:

https://cypherpunkpay.org/ - the easiest to install and maintain with explicit focus on privacy (disclosure: I am the author)
https://btcpayserver.org/ - the legendary, most comprehensive and most established one


36027: <p>Imagine a web page that wants to accept Bitcoin donations. A naive approach is to provide a single address on the page and that's it. The problem here is that the address is known to everyone and if the money ever moves, the address is being reused for new donations after the first spend.</p>
<p>OK, so how can we do better? We can have xpub and generate a new address every time ... every time what? The first basic strategy would be to generate a new address as soon as someone sends a donation to the previously generated one. The problem here is that an active monitoring of the web page by a robot simply reveals all the addresses again.</p>
<p>Can we do better? We can, for example, generate a new address per IP address. Doing this, the robot would always see one address that is dedicated to its IP address and unless someone from the robot's network under the same IP address donated, the robot could not reveal anything useful. The problem here is that if we assume not very high conversion rate, we generate a lot of addresses and most of them never receive any donation. This causes a problem with seed recovery, where there usually is a gap limit defined for the maximum number of empty addresses that are scanned before the scan is terminated. I assume this is quite wallet specific rule, but I assume in general, the problem exists here (does it really?).</p>
<p>So how can we do better? Say we want to use a cold storage using a 24 word seed + passphrase and we are happy to run Bitcoin Core together with the web page. How can we solve donation in a way that is manageable, can be recovered from seed, and at the same time optimizes for privacy? Does a solution already exists somewhere (for example BTCPayServer)? If so, how does it work and what are the privacy properties of such a solution?</p>
 -1: <blockquote>
<p>The first basic strategy would be to generate a new address as soon as someone sends a donation to the previously generated one.</p>
</blockquote>
<p>This is not a good strategy. Consider Alice opens the donation page, looked at the address and decided to send nothing. Bob will also see the same address and send some bitcoin. Alice can track this donation or at least know some information about the inputs and history related to transaction.</p>
<blockquote>
<p>Can we do better? We can, for example, generate a new address per IP address.</p>
</blockquote>
<p>Few <a href="https://github.com/RhinoSecurityLabs/IPRotate_Burp_Extension" rel="nofollow noreferrer">tools</a> are available to use different IP on each request. So, this may not be the best approach.</p>
<blockquote>
<p>The problem here is that if we assume not very high conversion rate, we generate a lot of addresses and most of them never receive any donation. This causes a problem with seed recovery, where there usually is a gap limit defined for the maximum number of empty addresses that are scanned before the scan is terminated. I assume this is quite wallet specific rule, but I assume in general, the problem exists here</p>
</blockquote>
<p>Yes, according to my understanding 'Gap Limit' is an issue. The biggest issue in trying to solve this problem of privacy in donation addresses.</p>
<blockquote>
<p>Does a solution already exists somewhere (for example BTCPayServer)? If so, how does it work and what are the privacy properties of such a solution?</p>
</blockquote>
<p>BTCPayServer creates a new bitcoin address every time you open the donation page.</p>
<p>Example: <a href="https://btcpayjungle.com/api/v1/invoices?storeId=FSt2N7kxe7m5Nzbu5xxqSB83faZQG44Etx2wp266C4rC&amp;price=10&amp;currency=USD" rel="nofollow noreferrer">https://btcpayjungle.com/api/v1/invoices?storeId=FSt2N7kxe7m5Nzbu5xxqSB83faZQG44Etx2wp266C4rC&amp;price=10&amp;currency=USD</a></p>
<p>I had created a new bitcoin wallet in electrum which has default gap limit of 20. I could find all the <a href="https://pastebin.com/raw/2Ky2LW1S" rel="nofollow noreferrer">20 addresses</a> in my wallet by opening the above link 20 times. 21st request to open the page returned a new address: <code>bc1qpzt7hax5cade0yu4grqn4aat62hdq9jj9cxuav</code> which I could not see in my wallet.</p>
<p>I had to increase the gap limit by running the below command, relaunch electrum to see the address:</p>
<pre><code>wallet.change_gap_limit(21)

</code></pre>
<p>So BTCPayserver is good for privacy in this case but will have issues managing wallet with huge gap limit if someone tries to attack.</p>
<p>Joinmarket donation page also provides a new bitcoin address every time you refresh the page: <a href="https://bitcoinprivacy.me/joinmarket-donations" rel="nofollow noreferrer">https://bitcoinprivacy.me/joinmarket-donations</a>. Chris Belcher shared on <a href="https://twitter.com/chris_belcher_/status/1404202506440384512" rel="nofollow noreferrer">twitter</a> that lot of addresses are imported in a watch-only wallet(Core).</p>
<p><strong>Other solutions:</strong></p>
<p>Reusable Payment Codes: <a href="https://support.samourai.io/article/68-what-are-paynyms" rel="nofollow noreferrer">https://support.samourai.io/article/68-what-are-paynyms</a></p>
<hr />
<p>This is an interesting problem to solve and I will update the answer if I find better solution or a way to use Bitcoin Core for donations which improves privacy and no other major issues.</p>
 123812: <p>Great question!</p>
<p>For each anonymous visitor who clicked [Donate]:</p>
<ul>
<li>generate a new, high-entropy URL that is public to access but impossible to guess (additionally, it can expire after some time)</li>
<li>derive a new Bitcoin address based on xpub and the counter; this address is dedicated to this specific donation attempt</li>
</ul>
<p>Even if anonymous visitor abandons the process without paying, her unique address is <strong>never reused</strong>. It is &quot;wasted&quot; in terms of the address space gap.</p>
<p>Other anonymous visitors <strong>will not</strong> be able to discover any past or future addresses because they can't guess the URL-s.</p>
<p>The only drawback is theoretical risk of pointless vandalism: one can click [Donate] in a loop, increasing the number of unused addresses in your wallet. This isn't really a big issue because most wallets can be configured to arbitrary gap. I also never heard of such vandalism attempts happening in practice.</p>
<p>Now, the most <strong>pragmatic</strong> way to go aobut it is use existing open source, self-hosted donations processors like:</p>
<ul>
<li><a href="https://cypherpunkpay.org/" rel="nofollow noreferrer">https://cypherpunkpay.org/</a> - the easiest to install and maintain with explicit focus on privacy (disclosure: I am the author)</li>
<li><a href="https://btcpayserver.org/" rel="nofollow noreferrer">https://btcpayserver.org/</a> - the legendary, most comprehensive and most established one</li>
</ul>
