Table of Contents
Quick answer: To remove shipping from one product, open it and tick Virtual in the Product data panel, which strips its shipping data. For physical items that should ship free, assign a shipping class and set its rate to 0 on your Flat Rate method. In mixed carts where some items still need shipping, hide methods with the woocommerce_package_rates filter or a conditional shipping plugin. The Virtual checkbox changes whether a product needs shipping at all, while classes and plugins only hide or zero the cost.
Key Takeaways
- The right method depends on the product: tick Virtual for items with nothing to ship, use a zero-rate shipping class for physical items that should ship free, and the woocommerce_package_rates filter for mixed carts.
- Marking a product Virtual removes it from shipping calculations, but a mixed cart still charges shipping for any physical item, so the Virtual checkbox alone won’t suppress shipping cartwide.
- A zero-rate shipping class makes a physical product add nothing to the shipping total, though a shipping line still shows at zero.
- When testing code, clear the cart and cache, since WooCommerce session-caches calculated rates and your snippet may look like it does nothing.
- Always keep a fallback method like Local Pickup available, since hiding every method leaves customers stuck at a “no shipping options” dead end. For conditional, no-code rules per product, category, or SKU, the Hide Shipping Method for WooCommerce plugin handles it from the admin.

Some products should not carry a shipping charge: digital downloads, services, bookings, gift cards, or items you fulfil another way. WooCommerce can do this, but the right method depends on whether the product is genuinely virtual or a physical item that simply ships free.
This post covers the native virtual checkbox, the shipping-class workaround, a developer code method, and the no-code plugin route, with the mixed-cart behavior that trips most people up.
When you would disable shipping for certain products
A few common situations call for turning shipping off on specific items:
- Digital or virtual goods: ebooks, software, memberships, online courses.
- Local-pickup-only items: products collected in person, never shipped.
- Service or booking products: consultations, repairs, tickets.
- Perishable or restricted goods you handle through a separate process.
Method 1: Mark the product Virtual
The simplest case is a product with nothing to ship.
- Open the product and find the Product data panel.
- Tick the Virtual checkbox at the top.
- Update the product.

A virtual product loses its Shipping tab and is ignored in shipping calculations. If the cart contains only virtual products, WooCommerce skips the shipping step and the shipping address fields entirely. Add Downloadable as well if buyers should receive a file. Note that Downloadable on its own does not remove shipping, only Virtual does.
The catch is the mixed cart. With one virtual and one physical item, WooCommerce still calculates shipping for the physical item, so a shipping charge still appears. The virtual item simply adds nothing. Marking a product virtual does not suppress shipping for the whole cart when a physical item remains.
Method 2: A zero-rate shipping class
For a physical product that should ship free while others are charged, use a shipping class.
- Go to WooCommerce > Settings > Shipping > Shipping classes and add a class such as “Free shipping.”
- Assign it to the product under Product data > Shipping > Shipping class.
- Open your zone’s Flat Rate method and set the cost for that class to 0.
A shipping class on its own does nothing, it only groups products. The zero cost is what makes that product add nothing to the shipping total, though a shipping line still shows at zero. For how classes and zones interact, see the WooCommerce shipping zones configuration guide.
Method 3: Hide shipping methods with code
When you need shipping to disappear entirely for carts containing certain products, filter the rates with woocommerce_package_rates:
add_filter( ‘woocommerce_package_rates’, ‘ds_hide_shipping_for_product’, 100, 2 );
function ds_hide_shipping_for_product( $rates, $package ) {
$no_ship_ids = array( 123, 456 ); // product IDs that should ship free
foreach ( $package[‘contents’] as $item ) {
if ( in_array( $item[‘product_id’], $no_ship_ids, true ) ) {
return array(); // hide every method for this package
}
}
return $rates;
}
Adjust the IDs, or branch on category with has_term() or on shipping class with $item[‘data’]->get_shipping_class().
One gotcha trips up almost everyone testing this: WooCommerce caches calculated shipping rates in the session, so your snippet may look like it does nothing on a cart that was already priced. Empty the cart and re-add an item, or toggle shipping debug mode, to force a fresh calculation while you test.
Method 4: Use a plugin
Code is fine for one fixed rule. A plugin is the better call when rules depend on conditions, vary by product, category, or SKU, or need no-code management. A conditional shipping plugin lets you hide shipping methods in WooCommerce based on cart contents, and you can layer several condition-based rules to hide WooCommerce shipping methods without touching code. Plugins hide or restrict methods, they do not change a product’s intrinsic shippability, which is still the job of the Virtual checkbox.
Hide Shipping for WooCommerce
Hide all other shipping methods when free shipping and/or local pickup are available in two minutes or less.
14-day, no-questions-asked money-back guarantee.

Fixing “no shipping options were found”
If you hide every method for a cart, the customer can hit a “no shipping options” message and stall. Keep at least a Local Pickup or a fallback Flat Rate method available, or pair the hide rule with a free method, so checkout never reaches a dead end. The full breakdown is in the guide to the no shipping options available error.
Best practices
Keep these in mind as you set things up:
- Use Virtual for products that never ship, and a zero-rate class for physical items that ship free.
- Always test a mixed cart, not just a single-product cart.
- Keep a fallback method available so hiding rules do not block checkout.
- Clear cart and cache when testing code, since rates are session-cached.
Conclusion
To disable shipping for certain products, match the method to the product: the Virtual checkbox for items with nothing to ship, a zero-rate shipping class for physical items that ship free, and the woocommerce_package_rates filter for mixed carts.
Test mixed carts and keep a fallback method so checkout never breaks. When the rules get conditional, the Hide Shipping Method for WooCommerce plugin sets them per product, category, or cart from the admin, with no code to maintain.
Frequently asked questions
How do I make a product not require shipping in WooCommerce?
Open the product, go to the Product data panel, and tick Virtual. The product is then excluded from shipping calculations.
What is the difference between Virtual and Downloadable?
Virtual removes shipping. Downloadable attaches a file the customer can download. A product can be both, but only Virtual affects shipping.
Why does shipping still show when I have a virtual product in the cart?
The cart also contains a physical product. WooCommerce only skips shipping when every item in the cart is virtual.
Can I disable shipping for one product but not others in the same cart?
Not by removing shipping outright, since the physical items still need it. Use a zero-rate shipping class on that product, or a plugin to hide methods conditionally.
Do I need a plugin to disable shipping for certain products?
No. The Virtual checkbox and shipping classes are native. A plugin only helps when you need conditional, no-code rules across many products.
Hide Shipping for WooCommerce
Hide all other shipping methods when free shipping and/or local pickup are available in two minutes or less.
14-day, no-questions-asked money-back guarantee.
