
WooCommerce shipping is the part of store setup where otherwise calm business owners start to cry. Products, payments, even tax usually go smoothly – and then they open Settings, Shipping, stare at zones, methods and classes, and either give up and charge one flat number for the whole planet, or set everything to free and quietly lose money on every heavy order. I have configured shipping for well over a hundred WooCommerce stores, from single-product shops in Germany to EU-wide stores with forty shipping rules, and I can tell you the system itself is fine. It is the explanation that is missing.
This guide is that explanation. I will give you the mental model that makes zones, methods and classes click, then walk through flat rates that actually cover your costs, free shipping thresholds that raise order values instead of eating margin, three fully worked real-world examples, live carrier rates and table-rate plugins for the stores that need them, label printing and fulfilment, EU cross-border paperwork, and the short list of mistakes that leak money in almost every store I audit. By the end you should be able to build a shipping matrix you trust – and test it before your customers do.
Table of contents
- The mental model: zones hold methods, classes adjust rates
- Setting up shipping zones properly
- Shipping classes: charging for product differences
- Flat rates that do not lose money
- Free shipping psychology and thresholds
- Three worked examples from real stores
- Flat, free, live or table rates: choosing your strategy
- Labels, fulfilment workflow and local pickup
- EU cross-border selling, IOSS and customs paperwork
- Common mistakes that leak money
- Shipping as a conversion factor – and testing your matrix
- Frequently asked questions
The mental model: zones hold methods, classes adjust rates
Everything in WooCommerce shipping hangs off one sentence: a customer’s address selects exactly one zone, that zone offers its methods, and shipping classes adjust what those methods charge. Once that sentence sits in your head, the settings screens stop looking arbitrary. Let me unpack the three layers.
- Zones are places. A zone is a geographic area you deliver to: “Germany”, “EU”, “UK”, “Rest of world”. Each customer address matches exactly one zone – the first one in your list that covers it. A customer in Munich never sees the rates of your EU zone if a Germany zone sits above it.
- Methods are offers inside a zone. Within each zone you add one or more shipping methods: flat rate, free shipping, local pickup, or a method added by a plugin (table rates, live DHL rates). The customer picks between the methods their zone offers at checkout.
- Classes are product properties. A shipping class is a label you attach to products – “heavy”, “bulky”, “letter post” – and every flat rate method can charge a different cost per class. Classes are how one rate table copes with the fact that a mug and a dumbbell do not cost the same to post.
The order of operations at checkout: WooCommerce reads the shipping address, walks your zone list top to bottom, stops at the first zone that matches, and shows that zone’s methods with costs calculated from the cart contents and their classes. That is the whole engine. Every problem I get called in to fix – wrong rates shown, free shipping appearing for the wrong country, a method missing – is one of those three layers misconfigured, and knowing which layer to look at is ninety percent of the fix.
One more structural note: methods and their costs live in zones, but classes are defined globally under Settings, Shipping, Classes, and assigned on the product edit screen (or in bulk via Quick Edit or CSV import). A class does nothing by itself – it only matters where a flat rate method references it. That split confuses people endlessly, so it is worth stating plainly.

Setting up shipping zones properly
Zones are configured under WooCommerce, Settings, Shipping. Three rules keep them sane:
- Order from specific to general. WooCommerce matches top to bottom and stops at the first hit. Put “Berlin postcodes” above “Germany”, “Germany” above “EU”, “EU” above “Europe non-EU”. If a broad zone sits above a narrow one, the narrow one never triggers – this is the single most common zone bug I find.
- Use regions and postcodes when countries are too coarse. A zone can be built from countries, states or provinces, and postcode lists or ranges – including wildcards, so 10* covers every postcode starting with 10. German island surcharges, UK highlands and islands, or a “local delivery” ring around your warehouse are all postcode zones.
- Treat “Locations not covered by your other zones” deliberately. This built-in catch-all zone applies to every address that matches nothing else. Leave it empty and customers from unmatched countries see “no shipping options available” and cannot buy – which is correct if you genuinely do not ship there. Put a method in it and you are offering that rate to the entire world, including countries where the postage alone costs three times what you charge. Decide which behaviour you want; do not let it happen by accident.
How many zones do you need? Fewer than you think. A typical German store I set up gets three or four: Germany, EU, Europe non-EU (Switzerland, UK, Norway), and either a Rest of world zone or nothing beyond that. Every zone you add is a row in your maintenance burden – rates change, carriers change, and each zone needs re-checking. Start coarse, split a zone only when its real postage costs actually diverge.
A detail that saves support tickets: zones match on the shipping address, which the customer may not have typed yet. On the cart page WooCommerce estimates from geolocation or the store default, so a visitor can see one rate on the cart page and another at checkout after entering their address. That is expected behaviour, not a bug – but it is a good reason to keep your rates simple enough that the estimate is rarely wrong.
Shipping classes: charging for product differences
Without classes, a flat rate method charges the same whether the cart holds a greetings card or a cast-iron pan. Classes fix that. Define them under Settings, Shipping, Classes – I usually create three to five, never more:
- Small or letter post – fits a padded envelope, ships as Grosbrief or large letter.
- Standard – the default parcel; often needs no explicit class at all because “no class” costs cover it.
- Heavy – above your carrier’s base weight band, where postage jumps.
- Bulky – oversized items that ship as freight or with a surcharge regardless of weight.
Then, inside each flat rate method, every class gets its own cost field, plus a “no shipping class cost” for unclassed products. The crucial setting is calculation type, and it has two options: “per class” adds up the class costs for every class present in the cart, while “per order” charges only the most expensive class cost found. Nearly every store wants per order – a cart with one heavy item and three small ones ships in one box priced by the heavy item, not four stacked charges. I have seen per class double shipping revenue on mixed carts and quietly wreck conversion until someone worked out why baskets were being abandoned at the shipping step.
Two habits make classes reliable. First, set the class in your product creation checklist or CSV template, because a new product with no class silently falls into the “no class” cost – fine if that cost is your standard rate, expensive if you left it at zero. Second, name classes after handling reality (“heavy”, “bulky”), not after products (“dumbbells”), so the scheme survives your catalogue growing. B2B stores selling by the carton or pallet push classes further – I cover that pattern in the WooCommerce B2B and wholesale guide.
Flat rates that do not lose money
Flat rate is the right default for most stores: predictable for the customer, predictable for you, and cheap to maintain. The trick is setting the number from your real costs rather than from a feeling. The formula I use with clients:
- Start with real postage. Take your last thirty shipments and note what each actually cost – carrier price, not the price list you remember. Most stores discover their average is one or two euros higher than they believed, because surcharges, fuel indexes and the occasional second parcel all count.
- Add packaging and handling. Box, filler, tape and label are typically EUR 0.60 to 1.50 per parcel; picking and packing time is real money even when it is your own evening. Add at least EUR 0.50 to 1.00.
- Decide your subsidy consciously. If real cost is EUR 6.40 and you charge EUR 4.90, you are subsidising EUR 1.50 per order – which is a legitimate marketing decision, as long as your margin absorbs it and you made it on purpose.
Flat rate cost fields also accept small formulas, which most owners never discover. The cost field understands [qty] (number of items) and fees, so 4.90 + 1.00 * [qty] charges a base price plus one euro per item, and [fee percent="5" min_fee="2"] charges five percent of the cart with a two euro minimum. For carts where each extra item genuinely adds postage – bottles, tins, books – a per-quantity formula beats a single number without needing any plugin. Keep formulas in the class cost fields too: heavy class 9.90, standard 4.90 + 0.50 * [qty], letter post 2.40.
Review the numbers twice a year. Carriers reprice every January in most of Europe, and a rate card set three years ago is almost certainly underwater now. A fifteen-minute check against your last invoice is enough.
Free shipping psychology and thresholds
Customers hate paying for shipping out of proportion to the amount involved – a EUR 4.90 fee feels worse than a EUR 4.90 price rise. That is why free shipping converts, and why doing it naively is expensive. The tool that keeps both sides happy is the threshold: free shipping above a minimum order value, added in any zone as a Free shipping method with “Minimum order amount” as the condition.
- Set the threshold just above your average order value. If your AOV is EUR 38, a free shipping threshold at EUR 50 gives a meaningful number of customers a reason to add one more item. Set it at EUR 35 and you give away shipping on orders you would have received anyway; set it at EUR 120 and nobody stretches for it, so it converts nothing. The working range is usually 20 to 30 percent above AOV.
- Advertise the gap. The threshold only works if customers see it: a cart notice like “Add EUR 7.60 more for free shipping” does the selling. WooCommerce does not show this by default; a few lines of code or a lightweight plugin adds it, and it is one of the highest-return snippets I install.
- Mind the interaction with other methods. By default, when free shipping qualifies, WooCommerce still shows the paid flat rate beside it, which is harmless but untidy. A well-known snippet hides other methods when free shipping is available – use it deliberately, and remember express shipping should usually survive the hiding.
- Coupons can grant free shipping too. The Free shipping method can require “a valid free shipping coupon” instead of, or as well as, a minimum amount. That gives you free-shipping promotions for newsletters and win-back emails without touching your zone configuration.
The one version of free shipping I push back on: free everywhere, unconditionally, on low-margin heavy products. If your average parcel costs EUR 6 to send and your average margin is EUR 11, unconditional free shipping hands over more than half your profit. Either bake the cost into prices (works for exclusive products, fails for price-comparable ones) or use a threshold. Free shipping is a pricing decision wearing a logistics costume – treat it with the same care as a discount.
Three worked examples from real stores
Abstract rules become obvious with numbers, so here are three configurations based on stores I have built, lightly rounded.
Example 1: light and heavy mix in one country
A client’s store sells tea (light) and cast-iron teapots and kettle sets (heavy), almost entirely within Germany. Real postage: letter-sized tea orders EUR 1.80, standard parcels EUR 4.20, heavy parcels EUR 7.50. Configuration: one Germany zone; classes “letter”, “heavy”, nothing for standard products. Flat rate method, calculation type per order: letter class EUR 2.40, no-class cost EUR 4.90, heavy class EUR 8.90. Free shipping method with a EUR 49 minimum (AOV was EUR 39). Result: small tea orders ship cheaply so the checkout does not scare off first-time buyers, heavy orders cover their real cost, and roughly a quarter of customers stretch past EUR 49.
Example 2: EU-wide store from one warehouse
A fashion accessories store shipping from the Netherlands to the whole EU plus Switzerland and the UK. Real costs cluster into bands, so the zones mirror the bands rather than listing every country separately:
| Zone | Countries | Standard rate | Free from |
|---|---|---|---|
| Domestic | Netherlands | EUR 3.95 | EUR 40 |
| EU near | DE, BE, LU, FR, AT, DK | EUR 5.95 | EUR 60 |
| EU far | Remaining EU countries | EUR 8.95 | EUR 80 |
| Europe non-EU | UK, CH, NO | EUR 12.95 | no free shipping |
Note the free shipping thresholds rise with the shipping cost – a flat EU-wide threshold would have made far-zone orders unprofitable. The catch-all zone is intentionally empty: the store does not ship outside Europe, and saying so clearly beats surprising a customer in Canada with a EUR 45 quote.
Example 3: bulky items that break the parcel model
A furniture accents store – mirrors, small tables, lamp stands. Anything over the carrier’s girth limit ships with a freight forwarder at EUR 34 to 60 depending on destination. Configuration: a “bulky” class on the freight items; in each zone the flat rate charges bulky class EUR 39 (domestic) or EUR 59 (EU), per order calculation; free shipping methods exclude carts by using a high minimum so bulky orders effectively never qualify. The honest lesson from this store: past a certain size, per-zone flat rates get rough, and the owner accepted charging slightly over cost on nearby freight and slightly under on distant freight, because the alternative – live freight quotes – would have added a plugin, an API contract and a failure mode for maybe twelve orders a month. Overkill declined.
Flat, free, live or table rates: choosing your strategy
Every store ends up mixing four pricing strategies, and the mix matters more than any single setting.
- Flat rates are the workhorse: zero external dependencies, instant checkout, predictable margins. Their weakness is granularity – one number per class per zone. Right for perhaps eight out of ten stores I see.
- Free above a threshold is a conversion and AOV tool layered on top of flat rates, not a replacement for them. Costed properly (previous section), it usually pays for itself; costed by vibes, it is the most common margin leak in small stores.
- Live carrier rates pull real-time quotes from DHL, UPS, FedEx or national carriers via a plugin and the carrier’s API. The customer pays close to true cost – which sounds fair but shows worse: real-time rates fluctuate, look expensive at checkout, and add an API call (and an API failure mode) to your shipping step. Worth it when parcels vary wildly in size and weight so no flat scheme fits, or when shipping cost is a large share of order value, as with heavy goods. Not worth it for a store shipping similar-sized parcels domestically – there the flat rate is simpler, faster and converts better.
- Table rates sit between the two: rule-based pricing on weight, cart total, item count or class combinations, via plugins such as Table Rate Shipping (the official WooCommerce extension) or Flexible Shipping by WP Desk, which has a capable free tier. Reach for them when you catch yourself wanting “EUR 4.90 up to 5 kg, EUR 7.90 up to 10 kg, EUR 12 above, but free over EUR 80 unless the cart has a bulky item”. That sentence cannot be expressed with core flat rates; it is one table row per condition in a table-rate plugin.
My honest decision rule: start with flat rates per class plus a free threshold, and move up only when a real order pattern – not a hypothetical – loses you money or sales for two months running. Complexity in shipping is a standing cost; every rule you add must earn its maintenance.

Labels, fulfilment workflow and local pickup
Configuring rates is half of shipping; the other half is getting parcels out of the door without retyping addresses. The workflow options, from smallest to largest store:
- Manual with copy and paste – viable up to perhaps five orders a day, and error-prone even then. If this is you, at least print packing slips from the order screen.
- Label plugins and platforms – services such as Sendcloud, Packlink or a carrier’s own WooCommerce plugin pull orders in, produce discounted labels, push tracking numbers back to the order and email the customer. For German stores, DHL’s official plugin is solid; shipcloud covers multiple carriers. Setup is an afternoon, and the per-label discounts often pay for the subscription by themselves.
- Fulfilment providers – at hundreds of orders a month, a third-party warehouse picks and packs; WooCommerce connects via the provider’s plugin or API. The shipping settings in your store then describe the fulfiller’s real rates, so revisit your whole matrix when you switch.
Whatever the scale, close the loop: mark orders completed when they ship, ensure tracking numbers reach the customer email, and check that your order status flow matches reality – customers judge shipping on communication as much as on speed.
Local pickup deserves a mention because it is free to offer and often forgotten. If you have premises – a shop, a workshop, even a market stall schedule – a Local pickup method in your domestic zone gives nearby customers a zero-cost option, brings people to the counter, and rescues orders where the postage would have killed the sale. Rename the method to something concrete (“Pickup in Freiburg – ready next working day”) so it reads as a promise rather than a mystery.
EU cross-border selling, IOSS and customs paperwork
Shipping inside the EU is operationally simple – no customs, just longer transit and higher postage. Crossing the EU border in either direction adds paperwork you should understand before you open the zone:
- Selling out of the EU (UK, Switzerland, Norway, rest of world): every parcel needs a customs declaration – CN22 for low-value parcels, CN23 or a commercial invoice above that – with contents, values and HS tariff codes. Label platforms generate these from order data if your products carry weights and, ideally, HS codes, which is a strong argument for a label platform the day you open a non-EU zone. Decide and display who pays import duties: DDU (customer pays on delivery, cheap for you, surprise for them) is the default; make it explicit on your shipping page to prevent angry emails and refused parcels.
- Selling into the EU from outside, or shipping EU orders from a non-EU warehouse: the IOSS scheme lets you collect VAT at checkout for consignments up to EUR 150 so the parcel clears customs without the buyer paying fees on the doorstep. IOSS is a tax registration, not a WooCommerce setting – your accountant registers it, your checkout charges destination VAT, and your customs forms carry the IOSS number. I mention it here because shipping and VAT setup are entangled at the border; plan them together.
- Returns across borders cost real money and admin. Many EU stores simply price non-EU zones a little higher to absorb occasional return chaos, which is cruder but saner than building a cross-border returns pipeline for a handful of orders.
None of this should scare you off cross-border selling – it is a growth lever – but open one border zone at a time, ship ten orders, and fix the friction before opening the next.
Common mistakes that leak money
The same faults appear in almost every shipping audit I do, so check your store against this list:
- Zone order wrong. A broad zone above a narrow one means the narrow zone never matches. Specific zones go first; test with a real address from each.
- The “everywhere” zone leaking. A method left in “Locations not covered by your other zones” quietly offers your domestic rate to every country on Earth. Either empty it or price it honestly.
- Classes forgotten on new products. The product added last month without its “heavy” class ships at the standard rate; multiply the shortfall by every order since. Put the class field into your product checklist and audit quarterly with a filtered product export.
- Calculation type never checked. Per class instead of per order silently stacks charges on mixed carts. Check it in every flat rate method – it defaults per class.
- Rates set once, never reviewed while carriers reprice annually. Diarise a January check.
- Free shipping threshold below AOV, giving away shipping on orders that needed no incentive.
- No weights or dimensions on products, which blocks live rates, label automation and customs forms later. Enter them from day one; retrofitting five hundred products is a miserable week.
- Testing only the happy path. The domestic standard order works; the mixed-class cart to Austria with a coupon is where the surprises live.
Shipping as a conversion factor – and testing your matrix
Unexpected shipping cost is the single most cited reason for cart abandonment in every study I have seen, and it matches what I see in clients’ analytics: the shipping step is where carts die. The fix is not always cheaper shipping – it is earlier and clearer shipping. State your rates on a shipping page and on product pages (“Free DE shipping over EUR 49”); let the cart page estimate shipping before checkout; show the free-shipping gap notice; and never let a surprise fee appear at the last step. A clear EUR 4.90 beats a hidden EUR 3.90. This interacts tightly with checkout design, which I cover in reducing cart abandonment at the WooCommerce checkout – shipping clarity is the first item on that list.
Before you call shipping done, test the matrix like a developer would, because your customers certainly will:
- One cart per zone: a real address in each zone, checking the expected methods and prices appear – and that unmatched countries behave as intended.
- One cart per class, plus a mixed cart, verifying the per-order calculation picks the right cost.
- Threshold edges: a cart at EUR 48.90 and EUR 49.10 against a EUR 49 threshold; with and without a free-shipping coupon.
- The estimate flow: cart page estimate versus checkout after entering an address, so you know what customers see in which order.
- A real order: place one, buy the actual label, and compare what the customer paid with what postage cost. That single comparison catches more pricing errors than any amount of settings-screen staring.
Shipping checks belong in your launch process next to payments and emails – my WooCommerce store launch checklist has the full pre-launch sequence, and the shipping section of it is essentially the list above.
Want your shipping set up properly?
I set up and untangle WooCommerce shipping as part of my WooCommerce development service – zones, classes, rate formulas, label workflow and cross-border setup, tested with real addresses and real labels, at EUR 15 per hour or a fixed price quoted within 24 hours. See the portfolio for recent stores, or send me your current shipping settings and I will reply within 24 hours with what I would change.
Frequently asked questions
How does WooCommerce shipping work?
A customer’s address matches exactly one shipping zone (the first matching zone in your list), the zone offers its shipping methods, and shipping classes adjust what each method charges per product type. Zones hold methods; classes adjust rates.
What is the difference between shipping zones and shipping classes?
Zones are places you ship to (Germany, EU, UK); classes are product properties (heavy, bulky, letter post). Zones decide which methods appear; classes decide what those methods cost for the specific products in the cart.
Why is my shipping method not showing at checkout?
Usually the address is matching a different zone than you expect – often a broader zone placed higher in the list, or the empty catch-all zone. Test with the exact customer address and walk your zone list top to bottom.
Should I offer free shipping in WooCommerce?
Offer it above a threshold set roughly 20 to 30 percent over your average order value, and advertise the gap in the cart. Unconditional free shipping only works when product margins comfortably absorb the real postage.
Do I need a plugin for WooCommerce shipping?
Not for most stores – core zones, flat rates with class costs and free shipping thresholds cover the majority of cases. Add a table-rate plugin for weight or rule-based pricing, and a label platform once you ship more than a handful of orders a day.
Are live carrier rates worth it?
Only when parcel sizes and weights vary so much that no flat scheme fits, or shipping is a large share of order value. For stores shipping similar parcels, flat rates are simpler, faster at checkout and convert better.
How do I charge more for heavy products?
Create a “heavy” shipping class, assign it to those products, and give the class a higher cost inside each flat rate method with calculation type set to per order – so a mixed cart is charged by the most expensive class once, not stacked.