WooCommerce: Disable Shipping Rate if Cart has Shipping Class

Our goal is to check if a Product with a specific Shipping Class is in the Cart, and consequently disabling Free Shipping if this is true. This is super useful when there are multiple items in the cart and you don’t want to give free shipping for certain orders.

WooCommerce: Disable Free Shipping if a specific Shipping Class is in the Cart
WooCommerce: Disable Free Shipping if a specific Shipping Class is in the Cart

PHP Snippet: Disable Free Shipping (for a Specific Shipping Zone) if Cart has Product with Specific Shipping Class @ WooCommerce Checkout

/**
 * @snippet       Disable Free Shipping if Cart has Shipping Class (WooCommerce 2.6+)
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @testedwith    WooCommerce 3.7
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */
 
add_filter( 'woocommerce_package_rates', 'businessbloomer_hide_free_shipping_for_shipping_class', 10, 2 );
  
function businessbloomer_hide_free_shipping_for_shipping_class( $rates, $package ) {
$shipping_class_target = 15; // shipping class ID (to find it, see screenshot below)
$in_cart = false;
foreach( WC()->cart->get_cart_contents() as $key => $values ) {
 if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
  $in_cart = true;
  break;
 } 
}
if( $in_cart ) {
 unset( $rates['free_shipping:8'] ); // shipping method with ID (to find it, see screenshot below)
}
return $rates;
}

Changes @ Shipping Not Showing after implementing PHP?

You probably need to clear the customer sessions:

Clear Customer Sessions – WooCommerce System Status Tools

WooCommerce: How to Find Shipping Class ID

WooCommerce 2.6+: Find Shipping Class ID
WooCommerce 2.6+: Find Shipping Class ID

WooCommerce: How to Find Shipping Method ID

Find Shipping Method Name in WooCommerce 2.6+
Find Shipping Method Name in WooCommerce 2.6+

Where to add this snippet?

You can place PHP snippets at the bottom of your child theme functions.php file (before "?>" if you have it). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such files - if you need more guidance, please take a look at my free video tutorial "Where to Place WooCommerce Customization?"

Does this snippet (still) work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7+.

If you think this code saved you time & money, please join other Business Bloomer supporters and avail of 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce Customization?

Check out these free video tutorials. You can start learning how to customize WooCommerce without unnecessary plugins. Watch me code and learn by example!

  • how-to-edit-woocommerce-with-php-snippets
  • woocommerce-hooks-add_action-list-visual
  • woocommerce-customize-single-product-page-PHP

Rodolfo Melogli

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as a WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza.

97 thoughts on “WooCommerce: Disable Shipping Rate if Cart has Shipping Class

  1. Hi Mr Rodolfo Melogli,

    I have a question here, is it possible to set that if certain shipping class product is in the cart, it only ship to New York, United States while the other states will not allow to purchase/ship if that particular shipping class product is in the cart.

    Thank you so much.

    1. Hello Su, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  2. Hi,

    I need a fairly simple solution:
    If parcel is less than 1.6kg show flat rate option.
    If parcel is greater than 1.6kg do not show flat rate option.

    I’ve tried to modify your code but having problems!

    Can you help please ?

    Best regards

    John

    1. Hello John, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  3. IS it possible to setup shipping classes example freight and that item can only be shipped to United States?

    1. Hello Tom, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  4. Hello,

    Thank you for this article, but I think you need to update him, because it doesn’t work if you set free shipping on order over $XX.

    Example:
    If you have two classes, “shipping class exclude free” ($9,99*[qty] Ship. fee) and “none class” ($10 Ship. fee) with free shipping on order over $20, Ship. cost = $29,99 instead of $9,99 (If you have 1 product exclude to shipping and 2 others products)

    1. Stan, thanks so much for your comment! Yes, this is possible – unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  5. Rodolfo: Can you give an example of this in an array? I would like to be able to use this for multiple items but cannot get it to work.

    1. Kevin, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  6. hi I found this snippet but is not working for me I mean I have a custom plugin and I am prettly sure that I am not putting the id in a right way if you can help

    1. Hey Sam, thanks so much for your comment! Unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  7. Hi! Thank you so much for this code. It works perfectly when the class is in the cart with another class. However, if the class is the cart alone, it can’t find the shipping method. If I add it to the same shipping zone, when the class is not in the cart the $0.00 shows. My shipping class is for free shipping and I’m trying to hide the $0.00 option in the cart when free shipping items are not in the cart.

    1. Hey Kamilah, thanks for your comment! My snippet works even with only one product in the cart, so there must be something else πŸ™‚

  8. Hey Rodolfo,

    I’ve got this set up on a site but need to add multiple shipping zones to check for and then exclude free shipping. I have it working when I just have one shipping class, but is three a way to add multiple classes to check for?
    I tried the following, but it didn’t work, I’m assuming it needs to be done differently:

    $shipping_class_target = 37; // shipping class ID (to find it, see screenshot below)
    $shipping_class_target = 36; // shipping class ID (to find it, see screenshot below)
    $shipping_class_target = 44; // shipping class ID (to find it, see screenshot below)

    When I do this, it seems to offer free shipping to everyone rather than exclude it.
    Is there another way to check for all three and then if one of them is present, not show free shipping option.

    Thanks πŸ™‚

    1. Hey Sarah, thanks for your comment πŸ™‚ In the way you put it, $shipping_class_target overrides itself and only stores 44. Instead, you should turn $shipping_class_target into an array() and then slightly change the function to look into an array (with the in_array() function). Hope this helps πŸ™‚

  9. This had worked for a while, but it seems to have stopped after the latest update where they turned the shipping settings to “legacy”. I think they no longer have ID’s connected to the classes? <input name="shipping_method[0]" data-index="0" id="shipping_method_0_legacy_flat_rate"

    1. Hello Ashton! That “legacy” thing looks weird to me, never seen it before. Please see screenshot in this page to find shipping ID

  10. Unfortunately, this did not work for me. Still gives the free shipping option even when an item with the ‘free shipping’ class isn’t in the cart.

    1. Hey Danny – thanks so much for your comment! This snippet disables a rate if the Cart contains a shipping class – if it doesn’t it does nothing πŸ™‚

  11. All I want to do is choose the shipping method when creating a product. When I click the dropdown under shipping and choose USPS, I want checkout to display calculated shipping and NOT free shipping. If I choose Free Shipping, I want checkout to display free shipping NOT calculated. Why is this so difficult??? Why do shipping zones? What’s the point of choosing a shipping class if it doesn’t even matter??

    1. Hey Anthony – thanks so much for your comment! Please try ask this to WooCommerce support team πŸ™‚

  12. Hi Rodolfo & team – thanks for your enthusiasm!
    Woo 3.3.5 / Flatsome: Can this problem be solved with a snippet (rather close to the issue above):
    When buying a product from a specified shipping class, e.g. heavy items (alternative: a specified “pick up” product category) , all other shipment methods except “pick up at stock” have to be eliminated visually on the checkout page. Meaning that only the “pick up” possibility is left on the checkout page, when 1 item (or more) from the “heavy class” is chosen – even if there are items from other shipping classes.

    1. Hey Egon, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  13. You saved 40 buck on Booster and a lot of stress :)) I’m not technical but with these tutorials feeling like backender πŸ˜€ Can’t thank you enough, awesome!

  14. Awesome! Just saved me a load of stress!

  15. HI Rodolfo
    How would that snippet look if you want the opposite e.g Enable free shipping if cart has a specific item or shipping class?

    1. Paul, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  16. You guys are grate. I loved this tutorial. Thank you for good stuff. BTW it works on WooCommerce 3.2.6 and wordpress 4.9.1

  17. Hey,
    Thanks for all the work you have done with this snippet!
    I have multiple shipping totals to use for each product, and i’m trying to use your code to remove the shipping options I don’t want to see for multiple classes.
    Unfortunately your code was designed with only one shipping option calculator in mind, so it hides the shipping options from both!
    Is there any way to set it to run for individual shipping calculations?

    1. Hey Dan, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  18. Hi Rodolfo, This snippet work perfectly for us. Thank you for all your work πŸ™‚

    Just wanted to know if you can help on the following scenario;

    – If the cart has multiple products, 1 product with the shipping class in your template already, but another product or products with other shipping classes – how do we then show those shipping classes in your CODE. We then want the free shipping to be unset since the other shipping class products has a flat rate cost and doesn’t belong to the free shipping delivery option hence the customer needs to pay for for delivery now for the products with the other shipping classes.

    With your help and this code, we know how to unset the shipping methods, but can’t figure out the above.

    Thank you!

    1. Hey Rodrigo, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  19. Works great! Avada theme v5.2.2, php 7.0.20, WordPress 4.8.3, Woocommerce 3.2.3
    Used it (modified) to exclude rush shipping method when used with a specific shipping class (a heavy item). It excludes the “Rush shipping” option whenever an item from the heavy item class is in the cart, regardless of other cart contents.

    Thank you!

  20. Does the php snippet work with a Child Theme functions.php? The file looks different, doesn’t have the”?>” at the end.
    Also what are these numbers ( 10, 2 ) for?
    I`m using a 3rd party plugin called table rate shipping. Will this code snippet work with that shipping method?
    I used this code and it did not work.

    1. Hey Chris, thanks so much for your comment! In answer to your questions:

      1. Yes, it works on a child theme
      2. The child theme functions.php file does not require the “?>” at the end
      3. The (10,2) numbers are required when using add_filter
      4. I don’t know if this snippet works with other plugins, this works for default WooCommerce

      Hope this helps a little πŸ™‚

    2. Hi, I’ve got it working with Table Rate Shipping but I had to ‘unset’ every rate…

      if( $in_cart ) {
       unset( $rates['mh_wc_table_rate_plus_85'] );
       unset( $rates['mh_wc_table_rate_plus_1' ] );
       unset( $rates['mh_wc_table_rate_plus_2' ] );
       unset( $rates['mh_wc_table_rate_plus_3' ] );

      etc

      Now I’m looking to find out whether you can only unset the rate if every item in the cart has the specified shipping class, so if it doesn’t, I don’t want to exclude the other rates, any ideas??

  21. Is this possible to modify for ‘virtual’ products?

    I have a problem of a free shipping threshold that gets triggered when there is a mix of ‘virtual’ and ‘physical’ products.

    I only want ‘physical’ products to trigger the free shipping and can’t seem to find anything on this…your article is as close as it comes.

    1. Mac, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

    2. Hi, I have the same problem as Mac above. I don’t want free shipping to be triggered if a customer has a mixed order of physical and virtual products. Is there a solution possible?

      1. Hello Iris, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  22. Hello Rodolfo,

    I used this snipet:

    With chrome it worked perfectly, but not with firefox and internet explorer. Can you help me?

    Thanks,
    Holger

    1. Holger, thanks so much for your comment! PHP is browser-independent, so try clearing the browser cache. Only thing I can think of πŸ™‚

  23. Thanks for the code! But I’ll throw you a curve ball. I’ve split my cart into two packages using some php. Essentially, I have products that can have free shipping (among other rates), and products that cannot have free shipping. Therefore I two packages to get separate rates for.

    How do I unset free shipping for only one of the two packages, the one containing $shipping_class_target?

    1. Hey Raymond, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  24. Worked for me perfect on Woocommerce 3.1.1 version. Excellent! You saved me $99 US Dollars

  25. Hi Rodolfo,

    I just found your blog today and you got my attention, lots valuable content.

    I have a situation that’s unique but maybe you can suggest if this could work or not.

    In woocommerce we have only one zone (USA) and one class (Free), flat rate class is there too but no product is using that class.
    Unique situation is that we have Dokan vendor installed so basically vendor is able to override shipping with their own per product if they choose.

    Problem is on checkout customer still gets option of main Woo Free shipping along with vendor shipping.
    My question is how could I remove main Woo shipping option so cart displays only vendor shipping?

    My thought is I can either would have remove default Woo shipping from the cart completely (not sure how)

    Or other thought was to setup shipping class call it “Vendor” class, let vendors select it and if that class is in the cart remove our “Free” shipping as an option using your code snippet.

    What do you think?

    Thank you so much for your time.

    1. Damir, thanks for your comment! I suggest you ask this question to Dokan support πŸ™‚

  26. Very great article, however in my case, i want to unset cash on delivery if a product among the products in a cart belongs to a specific shipping class.
    Please can you guide me through.
    Cause i don’t want to make a silly mistake.
    my code

    function pekky_unset_gateway_by_shipping_class($available_gateways){
    global $woocommerce;
    $shipping_class_target = 15; // shipping class ID (to find it, see screenshot below)
    $in_cart = false;
    
    foreach ($woocommerce->cart->cart_contents as $key => $values ) {
    if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {//we've found the suspect we're looking for
      $in_cart = true;
      break;//pull out
     } 
    }
    if($in_cart == true){//since it's true, it belongs to the suspect shipping class
        unset( $available_gateways['cod'] );//remove cod.
    }
         
     }
        return $available_gateways;
    }
    
    add_filter('woocommerce_available_payment_gateways','pekky_unset_gateway_by_shipping_class');
     
    
    1. Is that my snippet? πŸ™‚

    2. lol, sort off, yh, but a little editted. i tried it the way i wrote the code, but it seems not to work, when a product which belongs to the shipping class in the cart, it still shows the cash on delivery option.
      Is it because cash on delivery is the only enabled payment?

    3. hey, just checked it out again, it’s working, thanks for your time and tutorial, i really love learning more about how woocommerce works in the code way

  27. Exactly what I was looking for. Thanks! It works perfectly.

  28. Hi,

    I am looking for a code to:
    Disable flat rate shipping to a specific country if the cart has a product with a specific shipping class.
    So in that case only the pick up options stays visible.
    Hope you can help me out…

    Kind regards,
    Debby

    1. Debby, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here via the blog comments. Thanks a lot for your understanding! ~R

  29. Hello, not working for my Woocommerce 3.0.8 anymore.
    It was working before.
    Any advice? =)

    1. Hey Pavel, thanks for your comment! If you switch theme does that work?

  30. Hi Rodolfo,
    Thanks for your excellent WooCommerce code snippets!
    Is it possible to target multiple shipping classes with this snippet, rather than just a single shipping class, in:
    $shipping_class_target = SingleID;

    1. Joe, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here on the blog. Thanks a lot for your understanding! ~R

  31. This worked for me thank you. But it required that I have a second shipping method in a particular zone which always shows even when free shipping is available. Is there anyway around that? If an item in the cart has my excluded from free shipping class it only shows the second method which is great but how do i make it so one or all of the items in my cart are all free shipping items only the free shipping method is displayed? In other words if free shipping is available I only want to show free shipping.

    1. Hey Nicholas, thanks for your comment πŸ™‚ I’m a little confused :O – could you provide a few screenshots as I can’t understand if this is custom to your specs or is a bug in my code? Thanks!

  32. I need to hide free shipping per product based on location when shipping cost is available on the cart/chectout page. How can i do that.

    1. Eric, thanks for your comment! Unfortunately this is custom and cannot give an answer here on the blog comments – thanks for your understanding πŸ™‚

  33. Good day! Big thanks for awesome info, very usefull and uniqe. Can you help me with code to hide specific shipping method when all products in cart with shipping class?

    1. Alex, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here on the blog. Thanks a lot for your understanding! ~R

  34. Great code snippet.
    I have it working for one shipping zone.
    Is it possible to have it for two different shipping zones?

    I currently have ‘free_shipping:12’

    But I need to also have it apply to ‘free_shipping:11’

    Thanks so much

    1. Hey SArah, thanks for your comment! You can just add another “unset” line under the existing one – let me know πŸ™‚

  35. Placed the code in my functions.php and it breaks my entire site. What a waste of time.

    1. Hey Joe, thanks for your comment and sorry about your negative experience! I just tried the snippet and it works without breaking my website, so there must be something else that is conflicting or you haven’t pasted the right thing in your functions.php. If you activate WP_DEBUG, what error displays on the screen? Thank you!

  36. Hello Rodolfo Melogli..

    Firstly I thank you so much for what you have done so far by helping many people across the world solving their issues with woocommerce. At a point you have inspired me to get into the deeper cores of woocommerce.

    I have used the snippet to “Disable Free Shipping (for a Specific Shipping Zone) if Cart has Product with Specific Shipping Class”, and it worked pretty fine as well. But what i actually want is to disable standard shipping cost also when a product with a shipping class is added to the cart, and I want to display only the shipping class cost. (I want this to happen when only a shipping class product is being purchased individually).

    Let me explain…

    Say for example I have products = ProWSC (Product with shipping class), ProNSC (Product with No shipping class)
    My standard shipping cost is 30, and that for shipping class is 50.

    And now, if i add ProNSC to cart, it shows 30 (working fine)
    then, If i add both ProWSC and ProNSC to cart, it shows 80 (working fine)
    and now, if i add only proWSC to cart, it again is showing 80. ( I want it to display only 50 instead).

    Why actually I want to do so is, my products with shipping classes are above 1000 INR (indian Rupees) for which I offer free shipping. But they are heavy as well. That is why I want to charge them the shipping class charges.

    1. Hey Samrat, thanks so much for your comment! Yes, this is possible – but unfortunately this is custom work and I cannot provide a complementary solution here on the blog. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding! ~R

  37. Can I post my question here? If yes then..

    Lets say two products of my products as 1)SMALL PRODUCT and 2)BIG PRODUCT.

    I don’t want to charge shipping rate for SMALL PRODUCT if user buys both SMALL PRODUCT and BIG PRODUCT at once.

    How ever I want to charge shipping rate for SMALL PRODUCT if he is not buying the BIG PRODUCT. (ofcourse there are other products also in cart along with these)

    How can i achieve this?

    1. and the shipping method is live FedEx.

    2. Hey Mounika, thanks for your comment! This is pretty custom so unfortunately I can’t give you a fix right now. By the way, have you tried assigning the shipping class to the BIG product and use my snippet, which would disable free shipping if the BIG product is in the cart?

  38. Hello, Rodolfo

    Thank you for sharing this useful information.

    This worked for me on 2.6+ version. I have disable free shipping on some shipping classes. I have a question, how can I disable “No Shipping Class” because it’s show up in the cart and checkout page like two shipping methods. Have a look here: https://puu.sh/qBBj7/4a593e93dd.jpg

    For free shipping products I setted them without any shipping class & it’s show the title of flat rate as shown in screenshot. Is there any way to hide “No Shipping Class”?

    Thanks in advance!

    Regards

    1. Hello Abd Ur, thanks for your comment!

      If that shipping option has no shipping “class”, it must be a shipping “rate”. Free shipping is a shipping “rate” for example, and so is “flat_rate”.

      Just find the name of the rate (e.g. “shipping_rate_name”) and then:

      unset( $rates['shipping_rate_name'] );
      

      Hope this helps

      1. Thanks for response! I did fix this problem found useful info from docs Woo Theme. I’m just wondering why I didn’t get notification when you replied to my message. I came here just to look for response.

        Once again thanks!

        Regards,

        1. Excellent, great to know. Re: email, I have no idea – it could be you entered the wrong email address or that it went to spam maybe? Try again πŸ™‚

          1. Email is fine. I get notification about moderation but not about comment post. This too I came here to have a look.

            Well the important thing is problem is solved.

            Thanks

  39. With the new WooCommerce version (2.6.1) the shipping class id cannot be found this way anymore. Do you have another suggestion? Many thanks

    1. Thanks for your feedback Natascha – I’ll take a look and revert πŸ™‚

      1. Please let me know as well!!

        1. Will do David – thanks for the reminder πŸ™‚

        2. Updated! Let me know πŸ™‚

          1. Excellent! Didn’t get a notification either… I just found out I had already wrote a comment here πŸ™‚

            Great work Rodolfo! Thanks for sharing!

            1. Ah, weird! Awesome πŸ™‚

    2. Updated! Let me know πŸ™‚

Questions? Feedback? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between: [php]code_here[/php]. Failure to complying with this (as well as going off topic) will result in comment deletion. You should expect a reply in about a week - this is a popular blog but I need to get paid work done first. Please consider joining #BloomerArmada to ask me 1-to-1 WooCommerce questions. Thank you :)

Your email address will not be published. Required fields are marked *