Shipping configuration is the part of PrestaShop most merchants set up once, in a hurry, on launch day, and then never touch again, even as it quietly overcharges some customers and undercharges others on every single order. The reason it stays broken is that PrestaShop's shipping isn't one setting; it's four moving parts (zones, carriers, ranges, and cart rules) that have to agree with each other, and a misalignment between any two of them shows up at checkout as either a missing delivery option or a wrong price. Unexpected shipping cost at checkout is one of the most commonly cited reasons shoppers abandon a cart, so the back office screen you skimmed past on day one has a direct line to your conversion rate.
Last updated: June 2026.
This guide is the map of PrestaShop's shipping engine: the exact back-office paths, how zones, carriers and ranges actually fit together, the settings that silently break checkout, and the configuration gotchas that trip up experienced store owners. It deliberately stays on configuration. The deeper economic and carrier-specific questions, what to charge, which carrier to integrate, how to show delivery dates, each have their own guide, and you'll find them linked at the relevant point rather than half-covered here.
PrestaShop's shipping architecture: four parts that must agree
Before touching a setting, it helps to see how PrestaShop models shipping internally, because the back office hides the relationships that actually determine what a customer sees:
- Zones, geographic buckets (Europe, North America, Asia…). A zone is the unit a carrier prices against; you never price per-country directly, you price per-zone.
- Carriers, the delivery services shown at checkout (the Carrier object in the database, table ps_carrier). A carrier is enabled per-zone and carries its own price ranges.
- Ranges, every carrier prices either by weight (ps_range_weight) or by order value (ps_range_price), never both at once. The range rows define the brackets; the actual delivery prices are assigned per zone in the carrier's cost grid and stored separately in ps_delivery (per carrier, range and zone).
- Cart rules. Discounts and free-shipping conditions (ps_cart_rule) that can override carrier pricing for a given order.
The relationship that catches people: a carrier only appears at checkout when all three conditions line up. The customer's country is in a zone, that zone is ticked for the carrier, and the carrier has a price range that covers the cart's weight (or value). Break any link and the carrier silently vanishes from the customer's options. Most "why is there no shipping option for this customer?" tickets trace back to exactly one missing link in that chain.
Step 1. Get your zones right (International → Locations)
Go to International → Locations → Zones. PrestaShop ships a set of default zones, the continental buckets (Europe, North America, South America, Asia, Africa, Oceania, Antarctica) plus a separate Europe (non-EU) zone, and for most stores they're too coarse to price honestly. A zone is the finest granularity you can charge at, so if two countries with very different real shipping costs share a zone, you're forced to either overcharge the cheap one or eat the cost on the expensive one.
A more honest split for an EU-based store usually looks like: Domestic (home country, your cheapest rate), EU neighbouring, EU rest, Europe non-EU (Switzerland, Norway, UK, customs applies), North America, and Rest of world. After creating zones, assign each country at International → Locations → Countries. Open the country and set its Zone dropdown. A country belongs to exactly one zone, so this is also where you control whether a country is even sellable: a country with its status set to disabled won't reach checkout regardless of carriers.
So what? The closer your zones map to your real carrier invoice, the less margin you bleed on long-distance orders and the fewer customers you scare off with an inflated domestic-adjacent rate. This is the single highest-leverage thing on the shipping screen, and it costs nothing but ten minutes.
Step 2, Configure carriers (Shipping → Carriers)
Go to Shipping → Carriers → Add new carrier. PrestaShop's carrier wizard walks four screens; the fields that matter most:
- Carrier name, exactly what the customer sees at checkout. "DHL Express" and "Standard delivery" tell a shopper more than an internal code ever will.
- Transit time, the delivery estimate shown next to the option (e.g. "2–4 business days"). Be conservative: overpromising here is the fastest route to a negative review and a "where is my order" ticket.
- Speed grade (0–9), controls the sort order of carriers at checkout when sorting by speed. Give express services 8–9 and economy 1–3 so the list reads in a logical order.
- Logo, upload it. Shoppers recognise a DHL or UPS mark faster than they read a label, and a familiar carrier logo is a small, real trust signal at the moment of payment.
- Tracking URL, the carrier's tracking-page pattern with @ standing in for the tracking number (for example https://www.dhl.com/...?AWB=@). PrestaShop substitutes the order's tracking number into the @ when it builds the customer's link.
For the actual carrier connections, generating labels, syncing tracking, picking the right official or third-party module per courier. That's a topic in its own right; start with setting up carrier modules in PrestaShop, then the courier-specific guides: DHL, DPD and UPS, GLS multi-country, Colissimo for France, Mondial Relay relay points, and InPost Paczkomaty for Poland.
Step 3, Choose a pricing method and build ranges
On the carrier's Shipping locations and costs screen you make the one irreversible-feeling decision: weight-based or price-based ranges. A single carrier can only use one method, though you can run two carriers side by side using different methods.
| Method | Prices against | Best when… | PrestaShop table |
|---|---|---|---|
| Weight-based | Total cart weight (kg) | Products vary a lot in weight, furniture, electronics, food, anything where the courier charges by weight | ps_range_weight |
| Price-based | Total order value | Light, uniform goods where weight barely moves the cost, fashion, accessories, small electronics | ps_range_price |
Whichever you pick, you then create ranges and type a price into the grid for each zone the carrier serves. A weight-based example for a German store on DHL:
- 0–2 kg: Germany 4.99, EU 9.99, World 24.99
- 2–5 kg: Germany 5.99, EU 14.99, World 34.99
- 5–10 kg: Germany 7.99, EU 19.99, World 49.99
- 10–31.5 kg: Germany 14.99, EU 29.99, World 79.99
Two settings on this screen quietly decide whether checkout works at all:
- "Out-of-range behaviour", when a cart's weight or value lands above your highest range, PrestaShop either disables the carrier or applies the highest defined range. The default (disable) is the source of countless "no carrier available" complaints from customers with a big basket. If your top range is 31.5 kg and someone orders 40 kg, the carrier disappears unless you switch this to "apply the cost of the highest range", or add a higher range.
- Tax and shipping cost handling. Select the carrier's tax rule and whether handling costs are added to its shipping. After setting these, test a checkout to confirm shipping displays correctly tax-inclusive or tax-exclusive for each customer group; getting it wrong shows pre-tax shipping to one group and post-tax to another.
The range-gap trap
This is the classic PrestaShop shipping bug. A carrier can only price a cart whose weight (or value) falls inside one of its defined ranges; the moment a cart lands in an uncovered gap, the carrier disappears and the customer sees no shipping option. The trap is leaving an accidental hole between ranges. Defining 0–2 and then 3–5, say, leaves everything between 2 and 3 kg uncovered. Build ranges that butt up cleanly (0–2, 2–5, 5–10 with no overlap and no gap), and always test a cart whose weight lands exactly on a boundary and one just above your top range. A one-gram gap in a range table is invisible in the back office and very visible to the customer who can't check out.
Size and weight limits
Further down the carrier screen, Maximum package width / height / depth / weight hides a carrier when the cart exceeds the courier's real limits, so a 50 kg pallet stops being offered "standard letter post." Set these to match what each courier will actually accept; it prevents impossible orders rather than discovering them at the depot.
Step 4, Free shipping: two mechanisms, different jobs
PrestaShop gives you two genuinely different ways to make shipping free, and merchants routinely use the wrong one:
- Cart rule (Catalog → Discounts → Cart Rules). Set a condition (e.g. minimum order 50) and tick Free shipping under Actions. Left unrestricted, this frees shipping on every eligible carrier the customer could pick, and you absorb the cost, best for a blanket "free shipping over X" promotion. But a cart rule can also be scoped: under its Conditions you can restrict it to specific carriers, so it frees standard delivery while leaving express paid.
- A zero-priced range on the carrier. Set the shipping cost to 0 in a price range above your threshold, on that carrier only. This is the other way to offer free standard shipping while still charging for express. It lives on the carrier rather than on a promotion, so it applies as long as the carrier is offered.
There's also a global free-shipping override at Shipping → Preferences (free shipping above an order total / weight), which is the bluntest instrument of the three. Use it only when the rule is truly universal.
Where to set the threshold, and whether free shipping is even profitable for your margins, is a business decision, not a configuration one, and getting it wrong eats your margin faster than almost anything else in the store. We treat it properly in when free shipping makes sense and when it just eats your margin, and the broader pricing-model question, flat rate vs calculated vs free. In free vs flat rate vs calculated: what works best.
Step 5, Handling time and delivery estimates

At Shipping → Preferences you control the store-wide handling and free-shipping behaviour:
- Handling charges, a flat fee added to every shipment (packaging, labels). Most stores leave this at 0 and bake handling into their carrier rates so the customer sees one clean number.
- Free-shipping thresholds, the global "free shipping above an order total / weight" override discussed in Step 4.
The delivery estimate the customer reads, though, isn't set here. It comes from each carrier's transit time text back under Shipping → Carriers. Out of the box PrestaShop shows the string you typed ("2–4 business days"); it doesn't compute an actual delivery date around weekends, holidays, or a daily cut-off time.
That gap, between "what we typed" and "the actual date this lands on your doorstep", is exactly where most delivery-date anxiety lives, and it's a deliberate sibling topic rather than something to bolt on here. The reasoning and the customer-psychology case are in setting realistic delivery expectations and why a delivery-date feature reduces customer anxiety.
If you want PrestaShop to display a real, calculated delivery date rather than a static "2–4 days" string, one that respects your cut-off time, weekends, public holidays and warehouse calendar, on the product page and at checkout. That's what our mprestimateddelivery module does. So what does that get you? A shopper sees "Order in the next 3 hours, get it Thursday" instead of a vague range, which removes the single biggest "when will it arrive?" hesitation before they pay, and it's configured from your back office, not via a developer invoice or a theme edit, so it survives upgrades.
Step 6. A multi-carrier line-up that customers can actually choose between
Most healthy stores offer two or three carriers, but the trick isn't quantity, it's differentiation. Three carriers at the same price and the same speed just create decision paralysis at the worst possible moment. A line-up that reads clearly:
| Tier | Typical speed | Couriers | How to price it |
|---|---|---|---|
| Economy | 3–7 days | National post, relay-point services | At cost, the "I'm not in a hurry" option |
| Standard | 2–4 days | DPD, DHL, GLS | Small markup; pre-select this one at checkout |
| Express | 1–2 days | DHL Express, UPS, FedEx | Comfortable markup. Express buyers expect to pay |
Make sure each option shows its transit time beside the price, because customers weigh speed and cost together, not cost alone. The strategic question of how many tiers a small store should run, and where to land between cheap-and-slow and fast-and-dear, is its own balancing act, covered in shipping strategy for small stores, and the specific feasibility of offering rapid delivery in whether same-day and next-day delivery is realistic for small stores.
The configuration mistakes that silently cost money
Products with no weight set
Missing weights make weight-based rates inaccurate, a blank or zero weight usually produces a 0 kg cart, which quietly undercharges or mis-prices shipping, and only hides the carrier outright if no range covers 0 kg or the resulting cart weight. Set a weight (including packaging) on every product under its Shipping tab. A practical shortcut: weigh your bestsellers accurately, estimate the long tail, and refine over time. A wrong weight is better than a blank one.
A carrier with no zones ticked
A carrier serving zero zones shows zero options. If a whole country can't check out, work the chain backwards: is the country enabled, is it assigned to a zone, is that zone ticked on at least one active carrier, and does that carrier have a range covering the cart? It's almost always one broken link.
The default out-of-range behaviour
As above. Heavy or high-value carts disappearing at checkout is nearly always the "disable carrier when out of range" default biting a basket above your top range. Either extend the ranges or switch to "apply highest range."
Never test-ordering
Place real test orders across the awkward cases: a featherweight item, a heavy one, a multi-item cart, and a few different countries. A one-euro shipping error doesn't look like much until you multiply it by a thousand orders, then it's a thousand euros lost or overcharged, all from a number you never re-checked.
Tracking and notifications: where PrestaShop stops
PrestaShop's native tracking is deliberately minimal: you store a per-carrier tracking URL, and when an order moves to a "Shipped" status the customer can be emailed a link built from that pattern and the order's tracking number. What it doesn't do well out of the box is proactive, branded, multi-touch notification, and tracking communication is enough of a retention lever that it earns its own treatment in tracking numbers and delivery notifications.
When you ship from more than one place
Everything above assumes a single dispatch origin. The moment you hold stock in two warehouses, or dropship part of the catalogue, zones and ranges stop being enough. PrestaShop's legacy Advanced Stock Management and per-warehouse carrier associations covered this on PrestaShop 1.6 and early 1.7, but native ASM has since been dropped from modern 1.7+ core, so on current shops multi-warehouse handling typically falls to a dedicated module or ERP integration instead. That's a different configuration model with its own pitfalls; it's covered end-to-end in multi-warehouse management in PrestaShop.
Treat the shipping screen as a quarterly job, not a launch-day chore
Carrier invoices drift, fuel surcharges change, your average order value moves, and a zone split that was honest last year quietly starts losing you money. None of that surfaces on its own. PrestaShop will happily keep charging the rates you entered in 2024 forever. Block out half an hour a quarter to re-check your ranges against your actual carrier bills and re-run a few test orders. Shipping configuration isn't glamorous, but it sits directly between a customer deciding to buy and a customer actually paying. Which makes the few screens above some of the highest-return real estate in your whole back office.
FAQ
Why is no shipping option showing at checkout for a customer?
A carrier only appears when the whole chain lines up: the customer's country is enabled, that country is assigned to a zone, the zone is ticked on an active carrier, and the carrier has a range that covers the cart's weight or value. A break anywhere in that chain makes the carrier vanish silently. Work it backwards from the country, and check for a gap between ranges. The most common culprit is a cart weight landing in an uncovered hole (for example between a 0–2 kg range and a 3–5 kg range), which makes the carrier disappear with no error.
Can one carrier charge by weight and by order value at the same time?
No. Each carrier bills either by weight (ps_range_weight) or by price (ps_range_price), never both. If you need both behaviours, run two carriers side by side, one weight-based, one price-based, and let the customer's cart decide which is offered.
Where do I set the free-shipping threshold?
At Shipping → Preferences, in the Free shipping starts at price and weight fields. That threshold is global, it zeroes shipping on every eligible carrier above the value, including international ones. To make free shipping apply only to specific carriers or countries, use a cart rule under Catalog → Discounts with Free shipping ticked and a carrier or country restriction, or set a zero-priced range on a single carrier.
Why do heavy or high-value carts lose their shipping option above my top range?
That is PrestaShop's default out-of-range behaviour: when a cart exceeds your highest range, the carrier is disabled. On the carrier's Shipping locations and costs screen, switch the out-of-range setting to apply the cost of the highest range, or add a higher range to cover the heavier baskets.
Does PrestaShop show an actual delivery date, or just the transit-time text?
Out of the box it shows only the static transit time string you typed on each carrier (for example "2–4 business days"); it does not compute a real date around weekends, holidays or a daily cut-off. To display a calculated arrival date that respects those, our Estimated Delivery Date module handles it from the back office. The reasoning is in setting realistic delivery expectations.