Table Rate Shipping in WooCommerce: Step-by-Step Tutorial
A WooCommerce shipping table rate configuration fails most often before any rate is entered. The fault is zone resolution. WooCommerce evaluates shipping zones from the most specific match to the least specific match and applies only the first matching zone.

A postcode-based zone above a country-level zone is not a suggestion. It is a hard routing decision.
This matters when a store needs shipping by weight, cart value, quantity, or shipping class. A correctly built rate table can return one valid method for a 0.5 kg parcel, another for a 20 kg carton, and a third for a hazardous-goods class. A badly ordered zone returns the wrong method consistently and leaves no visible error in checkout.
The official WooCommerce Table Rate Shipping extension supports this model. Its current annual price is $119. It supports multiple rates per zone and conditions based on destination, weight, item count, shipping class, and price. The configuration is not difficult. The logic must be deterministic.
Shipping rules are evaluated in layers: zone first, method second, table row third. Debug them in that order.
Understand WooCommerce shipping zone evaluation before creating rates
Open WooCommerce → Settings → Shipping → Shipping zones. The visible zone list is the first part of the shipping engine.
WooCommerce reads zones from top to bottom. The first matching zone becomes active. Lower zones are ignored for that customer, even if they also match the address.
A stable configuration normally uses this sequence:
1. Most specific local zones first. Add postcode ranges, cities, or tightly defined regions at the top. A local courier rule for selected postcodes must be above the wider national zone.
2. Regional zones next. Use states, provinces, or grouped regions where freight pricing changes materially.
3. Country-level zones below them. These catch addresses not handled by the more specific definitions.
4. Rest of the world last. This should be a deliberate international fallback, not a dumping ground for untested rules.
For example, assume a store ships from the United States and uses a local same-day carrier for selected California postcodes. The local California postcode zone must precede a generic United States zone. If the United States zone appears first, the local rate will never be reached.
Do not create overlapping zones and then expect WooCommerce to compare shipping prices between them. It does not. It stops at the first geographical match.
Verify the active zone before diagnosing a table row
Use a test customer address matching one zone at a time. Check the cart and checkout after changing the shipping destination. If the expected shipping method is absent, do not begin by editing weight thresholds. Confirm the matching zone first.
A useful test matrix includes:
| Test case | Destination | Expected zone | Purpose |
|---|---|---|---|
| Local delivery | Defined postcode | Local postcode zone | Confirms specific-zone precedence |
| Standard domestic | State outside local range | Domestic regional or country zone | Confirms fallback within the country |
| International | Supported foreign country | International zone | Confirms cross-border method availability |
| Unserved address | Country outside configured coverage | No matching shipping method or explicit fallback | Confirms the store does not underquote shipping |
Test with a fresh cart session where possible. Cached fragments, persisted customer sessions, and address data from earlier tests can obscure the actual zone result.
Configure the official WooCommerce Table Rate Shipping extension
After the extension is installed and activated, add it to the shipping zone that should expose the rates.
Go to WooCommerce → Settings → Shipping → Shipping zones, select the required zone, then choose Add shipping method. Select Table Rate. Open the method settings after it has been added.
The method configuration has two different jobs:
- Method-level settings define the scope and calculation behavior.
- Table rows define the actual conditions and charges.
Do not put unrelated shipping logic into one global table. A domestic parcel table, a bulky-item table, and an international table usually belong in separate shipping zones or separate methods. The more conditions forced into a single rate list, the more likely an overlapping row will produce a rate that is technically valid but commercially wrong.
Set the method baseline
Start with the fields that affect all rows:
- Method title: This is customer-facing. Use a precise label such as “Standard Ground” or “Freight Delivery.” Avoid labels that conceal service limitations.
- Tax status: Set this according to the store’s tax treatment for shipping. This is not a display option; it affects totals.
- Handling fee: Apply only where it reflects an actual operational cost. A handling fee on every method can distort threshold pricing.
- Calculation type: This determines how WooCommerce aggregates table rows across cart contents. It is the most consequential setting in the method.
Then add rates. Each row should represent one measurable rule: a weight interval, an order subtotal interval, a shipping-class condition, or an item-count interval.
For a basic WooCommerce table rate shipping by weight implementation, use non-overlapping thresholds. Example:
| Condition | Minimum weight | Maximum weight | Shipping charge |
|---|---|---|---|
| Parcel 1 | 0 kg | 1 kg | $6 |
| Parcel 2 | 1.01 kg | 5 kg | $10 |
| Parcel 3 | 5.01 kg | 10 kg | $16 |
| Heavy parcel | 10.01 kg | 20 kg | $28 |
The exact field labels vary slightly by extension version and configuration, but the rule design does not change. A cart at 5 kg must have an unambiguous result. If one row ends at 5 kg and the next begins at 5 kg, test the boundary explicitly. Do not assume the extension interprets both limits the way the rate author intended.
Build rows from operational constraints, not marketing thresholds
Shipping tables should correspond to carrier cost breaks, warehouse packaging limits, or fulfillment constraints.
A threshold such as “free shipping above $50” is valid only if the additional margin covers the expected shipping cost. A threshold such as “free shipping above $49.99 because competitors use it” is not a shipping rule. It is an untested promotion.
Common conditions for table rows include:
- Cart price: Suitable for free-shipping thresholds and value-based subsidies.
- Weight: Suitable for carrier brackets and packaging cost changes.
- Item count: Suitable for flat per-unit fulfillment costs, low-value consumables, or samples.
- Shipping class: Suitable for bulky, fragile, refrigerated, hazardous, or surcharge-bearing products.
- Destination: Usually handled at zone level, where it is easier to audit.
Avoid encoding all five conditions into every row unless the business genuinely needs that granularity. Complex rows increase the number of test cases exponentially.
A shipping table is a pricing ruleset. Every overlap is a production defect waiting for a cart that combines the right products.
Select the correct calculation type
The official extension provides four calculation types: Per order, Calculated rates per item, Calculated rates per line, and Calculated rates per shipping class. They do not produce equivalent totals.
The most common configuration error is selecting a calculation type based on its label rather than on the store’s fulfillment model.
| Calculation type | What WooCommerce evaluates | Suitable use case | Failure mode |
|---|---|---|---|
| Per order | The cart as one shipping unit | One parcel policy based on total cart weight or value | Undercharges carts containing separately shipped products |
| Calculated rates per item | Each physical item separately | Per-unit shipping charges | Multiplies cost quickly for quantity-heavy carts |
| Calculated rates per line | Each unique cart line once, regardless of quantity | One shipping charge per distinct SKU line | Undercharges if every unit requires separate packing |
| Calculated rates per shipping class | Totals and evaluates each shipping class independently | Mixed carts with distinct handling classes | Requires disciplined class assignment |
Per order: the default for unified parcels
Choose Per order when the warehouse combines eligible products into one shipment and the shipping charge should reflect the cart as a whole.
A customer buys three items with a combined weight of 7 kg. The table should evaluate 7 kg once, then return the corresponding rate. This is the correct model for a standard parcel workflow.
Per order becomes hazardous when the cart contains products with different shipping classes. The extension resolves class conflicts by applying the rate of the shipping class with the highest priority. Priority 1 is highest.
That behavior is deterministic. It can still be commercially incorrect if priorities were assigned casually.
Calculated rates per item: each unit is a charge event
Choose Calculated rates per item when every item creates its own incremental shipping cost. Examples include individually mailed replacement parts, printed materials, or products packed in separate manufacturer cartons.
If a row produces $4 per item and the cart contains four units, the result is $16 before other applicable logic. This is not the same as a per-order $4 rate.
Do not use this calculation type as a shortcut for “shipping gets a little more expensive as quantity rises.” Build quantity bands instead if the warehouse packs multiple units together.
Calculated rates per line: evaluate the SKU line, not quantity
A cart line is a unique product line. If the shopper purchases five units of the same SKU, a per-line calculation evaluates that line once. If the shopper purchases one unit each of five different SKUs, it evaluates five lines.
This is useful where a pick-and-pack or supplier surcharge applies once per product type, not once per physical unit. It is also easy to misread.
Per line and per item must never be treated as interchangeable. The difference appears immediately when quantity exceeds one.
Calculated rates per shipping class: isolate incompatible handling rules
Choose Calculated rates per shipping class when products cannot be sensibly priced as one parcel category. A store selling apparel, furniture, and fragile glassware should not allow a lightweight t-shirt to pull a freight-priced table down to an ordinary parcel rate.
Create shipping classes under WooCommerce → Settings → Shipping → Shipping classes. Assign each relevant product a class. Then define rate rows that match the class and its applicable thresholds.
This architecture works only if product data is maintained. An unclassified bulky product bypasses the bulky-product logic. The checkout then returns a rate based on whatever remaining conditions match.
Handle shipping class priorities without masking costs
For the Per order calculation type, mixed-class carts are resolved through shipping class priorities. The class with priority 1 has the highest precedence.
This is appropriate where one class must control the entire shipment. For example, a cart containing a standard product and a restricted product may require the restricted-product delivery method to govern the complete order.
Configure the logic deliberately:
1. Identify classes that make other methods invalid. Hazardous materials, oversized freight, and temperature-controlled products are typical examples.
2. Give those restrictive classes the highest priority values. Use 1 only for the class that must win every mixed-cart conflict.
3. Place ordinary parcel classes below restrictive classes.
4. Test mixed carts, not only single-class carts. Most priority defects are invisible in isolated product tests.
5. Verify that the winning rate recovers the actual fulfillment cost. A class priority is not a surcharge mechanism unless the table rows include the surcharge.
A common error is assigning a high priority to the least expensive class because it is the most frequently sold. In a mixed cart, that can suppress the rate needed for the expensive item. The result is margin leakage, not a customer-friendly shipping policy.
Use shipping classes for physical differences
Shipping classes should describe fulfillment behavior. “Oversized,” “fragile,” “frozen,” and “standard parcel” are useful classes because they correspond to packaging, carriers, or service constraints.
Classes such as “high-margin,” “holiday bestseller,” or “products we want to promote” are usually a sign that merchandising logic is being pushed into the shipping engine. Use coupons, promotions, or pricing rules for those cases. Shipping classes should remain auditable by warehouse and support staff.
Test the rate table at its boundaries
A rate table cannot be validated by adding one product to the cart and observing one price. Test every transition point and every combination that changes the calculation type.
Use a structured sequence:
1. Test the lower and upper boundary of each row. For a 1 kg threshold, test just below 1 kg, exactly 1 kg, and just above 1 kg.
2. Test a cart with no assigned shipping class. This exposes products that fall through class-specific logic.
3. Test each shipping class independently. Confirm the expected method and price.
4. Test mixed shipping classes. This validates priority behavior for Per order and aggregation behavior for Per shipping class.
5. Test quantity variation. Use one unit, multiple units of the same SKU, and multiple distinct SKUs. This separates per-item from per-line behavior.
6. Test every active zone. A valid domestic table does not prove that the international zone is correct.
7. Test the cart and checkout. Themes, checkout extensions, caching layers, and payment flows can expose shipping methods differently.
Record the expected result before testing. A test without an expected rate is observation, not verification.
If a rate is absent, inspect in this order:
- Customer shipping address and the matched zone.
- Shipping method enabled inside that zone.
- Product weight, dimensions where relevant, and shipping class assignments.
- Table-row condition ranges and possible gaps.
- Calculation type.
- Shipping class priorities.
- Caching and persistent customer session data.
The sequence matters. Editing table rows before validating zone selection creates unnecessary configuration drift.
Use Flat Rate only for simple formulas
WooCommerce’s built-in Flat Rate method can calculate basic dynamic charges. It supports placeholders such as [qty] for quantity and [fee percent='X'] for a percentage of the order total, with min_fee and max_fee limits.
That is sufficient for simple policies: a fixed base cost plus a fee per quantity, or a percentage-based handling amount.
It is not a substitute for a flexible table rate shipping WooCommerce setup when the store needs combinations of weight bands, shipping classes, destination constraints, and cart-value thresholds. At that point, Flat Rate formulas become opaque and difficult to audit.
Use the smallest rule engine that accurately represents the operation. A one-line Flat Rate formula is easier to maintain than a 30-row table. A 30-row table is safer than forcing a multi-dimensional shipping policy into a formula field it cannot express.
Evaluate third-party table rate shipping plugins
The official extension is not the only option. Flexible Shipping by Octolize provides a free version, while its premium tier starts at $99 per year. PH Advanced Table Rate Shipping Pro starts at $39 for a single site.
Pricing is not the primary selection criterion. Inspect the condition model, compatibility with the store’s checkout stack, import/export workflow, and maintenance status before migrating a production rate table.
Use this comparison as a baseline:
| Option | Entry price | Best fit | Operational consideration |
|---|---|---|---|
| WooCommerce Table Rate Shipping | $119/year | Stores needing the official extension’s zone and calculation model | Direct alignment with WooCommerce shipping architecture |
| Flexible Shipping | Free version available; Pro from $99/year | Stores starting with simpler rules and expanding into advanced conditions | Confirm which required conditions are premium-only |
| PH Advanced Table Rate Shipping Pro | From $39 for one site | Budget-sensitive stores needing advanced rate logic | Test compatibility with theme and checkout extensions |
Before replacing an extension, export or document every existing rule. Capture zones, table rows, shipping classes, priorities, and expected rates for representative carts. A plugin migration is a pricing migration. It requires regression testing, not only activation testing.
Do not assume one plugin’s “weight” condition uses the same inclusivity rules, row precedence, or class aggregation model as another plugin. Rebuild the test matrix after migration.
Keep the shipping table maintainable
Shipping tables become unreliable when they are edited by multiple people without a rule convention. Use a naming and ownership model.
Each method title should identify the service. Each shipping class should identify a physical constraint. Each rate row should map to a carrier bracket, packaging threshold, or deliberate commercial policy. If a row cannot be explained in one sentence, it is not maintainable.
Review the configuration whenever any of these change:
- Carrier tariffs or surcharge structures.
- Warehouse packaging rules.
- Product weights or shipping-class assignments.
- Free-shipping thresholds.
- New countries, postcodes, or delivery regions.
- Checkout, caching, or shipping-plugin updates.
The expected baseline is straightforward:
- One customer address resolves to one shipping zone.
- Each cart produces a rate determined by an explicit table rule.
- Weight and price intervals have no accidental gaps or overlaps.
- Per item, per line, per order, and per shipping class calculations are selected for distinct fulfillment models.
- Shipping class priority
1is assigned only where that class must control a mixed cart. - Boundary carts and mixed-class carts return the documented totals.
A WooCommerce shipping table rate configuration is reliable when its output can be predicted before opening checkout. That is the standard. If a rate surprises the store operator, it will eventually surprise the customer at a higher cost.