Ahosting Logo
Knowledge Base

How to Sell Digital and Downloadable Products

Two things that give a digital product awayA file in a normal directory· under public_html, reachable by anyone who guesses the name· and search engines occasionally index itA shared link· a download link forwarded once is public· unless downloads are limited and expireWhat to setStore files outside the web root and serve them through the store, cap the download count, andset an expiry. Large files belong somewhere built for them.

WooCommerce sells downloadable files without a plugin. The mechanics are simple; what people get wrong is where the files live and how long the link stays valid, and both mistakes give your product away.

Setting a product up

In the product data panel, tick Virtual and Downloadable.

Virtual removes shipping from the equation, which is what stops a customer being asked for an address and a delivery method they do not need.

Downloadable reveals the file fields. Add one or several files; a customer receives links to all of them.

Two settings sit underneath, and they are the ones that matter.

Download limit and expiry

Download limit caps how many times the file can be fetched. Leave it blank for unlimited.

Download expiry sets how many days the link works after purchase. Blank means forever.

Both are frequently set too tightly, and the result is support tickets from legitimate customers. A download that failed halfway consumes an attempt. A customer who changes computer a year later finds a dead link.

A limit of three to five and an expiry of a year is generous enough to avoid the tickets and tight enough to stop a link being posted publicly and used indefinitely. Unlimited on both is a reasonable choice for a small shop where support time costs more than the occasional shared file.

Where the file must not be

The mistake that gives your product away entirely.

A file placed in a normal directory under public_html is reachable by anyone who knows or guesses the URL. WooCommerce's link is then a convenience in place of a control, and the file can be shared directly, bypassing your shop.

WooCommerce uploads files to a protected directory with a randomised name and rules preventing direct access. Use that, upload through the product screen rather than putting files somewhere yourself.

Then verify it. Copy the file's direct URL and open it in a private window without buying anything. You should be refused. There is more on why placement matters more than permissions here in understanding file permissions.

The three file access methods

Under WooCommerce's product settings there is a choice of how downloads are served, and it has real consequences.

Force downloads. PHP reads the file and sends it. Most secure (the real path is never exposed) and it holds a PHP process for the whole transfer. On a shared plan, several people downloading a large file at once can exhaust your concurrent processes and slow the whole site.

X-Accel-Redirect / X-Sendfile. PHP authorises and the web server delivers. Secure and efficient, and it requires server support. This is the right choice when available.

Redirect only. The customer is sent to the real file URL, which is then visible and shareable. Do not use this for anything you are selling.

Large files change the calculation

Anything above a few hundred megabytes strains the first method, and the failure is a download that stops partway.

The causes are PHP execution time limits and memory, and raising them is a partial fix in place of a solution. For the values, see configuring PHP settings.

For genuinely large products, external storage with signed temporary URLs is the arrangement that works: the file is served by something built for it, and the link still expires. That is a plugin or an integration rather than core WooCommerce.

Downloads are granted at a specific status

By default the link becomes available when the order reaches completed, not processing.

So a shop leaving paid orders at processing has customers who paid and received nothing. There is a setting to grant access on payment instead, and for a purely digital shop that is usually what you want.

Otherwise orders must be moved to completed, automatically for digital products, or by hand, which nobody does reliably at the weekend. Understanding order statuses walks through which status does what.

The email carrying the link

The completed-order email is what delivers the download. If that mail does not arrive, the customer has paid and has nothing.

This makes mail delivery a business-critical dependency instead of a nicety. Send through authenticated SMTP rather than the server's default, and confirm the message arrives at a major provider rather than assuming. Fixing WordPress email not sending explains why mail is often accepted and then discarded.

Also make sure the customer can reach their downloads from their account page. A visible link there means a lost email is an inconvenience instead of a refund.

Licences and taxes

Two things WooCommerce does not do for you.

A licence key per sale needs a plugin. So does any check that the key is valid when the software runs, selling keys without validating them is selling text.

Digital goods are also taxed differently in several jurisdictions, often by the customer's location rather than yours. That is a plugin and a decision instead of a setting. There is more on the mechanics in configuring tax settings.

Test the whole path

Buy your own product with a real payment, download the file, and check it opens.

Then check three things people skip: that the file's direct URL is refused, that the download link in the account page works, and that the email arrived somewhere other than your own domain.

That is ten minutes and it covers every way this fails.

Confirm the file cannot be reached directly

The whole arrangement rests on the file being unreachable except through the store, and that is worth testing rather than assuming.

curl -sI https://example.com/wp-content/uploads/2026/08/product.zip | head -1
curl -sI https://example.com/wp-content/uploads/woocommerce_uploads/product.zip | head -1
ls -la ~/public_html/wp-content/uploads/woocommerce_uploads/.htaccess 2>/dev/null

A direct request must be refused. If it returns the file, the address is guessable and the protection is decorative, since anyone who buys once can share the address with everybody.

Keeping the files outside the web root entirely is the stronger arrangement, because it does not depend on a rule file that a migration, a plugin or a server change can remove.

Watch what happens after the sale

Digital delivery fails quietly, since a customer who never received a link frequently does not report it.

wp db query "SELECT COUNT(*) FROM wp_woocommerce_downloadable_product_permissions WHERE download_count = 0;"
grep -ci 'download' wp-content/uploads/wc-logs/*.log 2>/dev/null | tail -3

Permissions granted and never used are the number to watch. A few are normal. A run of them starting on a particular date means delivery broke then, and the date points at whatever changed.

The usual causes are the confirmation email being filtered, the order sitting in a status that does not grant access, or an expiry that is shorter than the time customers actually take to download. Handling failed and pending payments deals with the status question.