Shipping is one of the most complex aspects of e-commerce. You have to worry about shipping provider regulations, large products that have awkward dimensions & box sizes, small products that the shipping calculators incorrectly estimate and you over charge, or you could have a mix match of product sizes and it's hard to come up with a fair flat rate shipping fee.
One of the approaches many store owners take is to mark up their products a little more and then offer free shipping. I think this is brilliant idea, but what happens if you have many small items and one large item that throws off the flat rate fee? If you're using WooCommerce there's a filter you can use to disable free shipping when a specific product is in the cart.
You simply have to get some information about your products, add that information to a little code snippet, and then put it in your functions.php file in your theme.
Getting the Product ID
The only information you need about the product(s) is the product id. This can be done easily by going to the Product page and then editing any existing product. You'll see the ID in the address bar.
Add Product IDs to Snippet
Now that you have any IDs that you need you just need to add them to the $ineligible array in the following snippet. Then add this snippet to your functions.php file in your theme and upload it to your website.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Disable free shipping for select products | |
* | |
* @param bool $is_available | |
*/ | |
function my_free_shipping( $is_available ) { | |
global $woocommerce; | |
// set the product ids that are ineligible | |
$ineligible = array( '14009', '14031' ); | |
// get cart contents | |
$cart_items = $woocommerce->cart->get_cart(); | |
// loop through the items looking for one in the ineligible array | |
foreach ( $cart_items as $key => $item ) { | |
if( in_array( $item['product_id'], $ineligible ) ) { | |
return false; | |
} | |
} | |
// nothing found return the default value | |
return $is_available; | |
} | |
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'my_free_shipping', 20 ); |
Disabling Other Shipping Methods
If you offer free shipping why would also offer paid shipping? Especially if it's the same shipping provider. The snippet I provided works even better once you hide the other available shipping options when free shipping is available.
I hope this article makes your store a little more shipping friendly. 🙂
Photo Credit: Ricecracker. via Compfight cc
Awesome snippet Patrick!
Great resource, thanks Patrick!
Hi – I need to hide free shipping based on location. How do I do this? Basically what I need to do is offer free shipping only to local customers, and revert to the API plugin Canada Post I have for all other orders. It seems like a simple thing to accomplish, but for the life of me, I can’t figure it out without a snippet of some kind in the theme, but I don’t want to mess up the code… Please help! I’m willing to pay.
Thanks,
Rocket Robin
Hey Rocket,
Off the top of my head I’m not sure how to do that. Have you looked into the default Local Pickup or Local Delivery shipping methods? There’s also the Local Pickup Plus extension.
Hi Patrick,
Thanks for your quick response! Actually, I solved my own problem. If you would suffer me to explain the solution to any other interested readers with the same issue, here it is: Simply, disable the free shipping option, and enable the “Local Delivery” option. In the Local Delivery option, rename it to “FREE DELIVERY” and add the postal codes with wildcards for whichever postal zone you offer free delivery. The time factor involved here is determining which postal codes those are. But the wildcards make it easy; for example, in my case, all of Toronto, Canada can be summarized as” M*”. No doubt this will be very valuable information to many, as it has taken me quite a while to solve this very simple problem…. if anyone has any questions or comments, let me know… http://www.rocketrobin.ca
Rocket that’s an awesome solution. Thanks for sharing! 🙂
Hi Patrick,
This sounds extremely useful, it is exactly what I was looking for. However, when I tried it on my website I got the following error: Parse error: syntax error, unexpected on line (whichever line this function started at).
I have 26 products that I need to disable free shipping for.
I enabled free shipping on my website for orders over $100 and then placed this snippet on my function.php file placing the 26 product ID numbers here:
// set the product ids that are ineligible
$ineligible = array( ‘14009’, ‘14031’, ‘etc’, ‘etc’ );
After updating my whole website went down because of the error and had to log in from my hosting to change the function.php back to normal….. I really like the sound of this solution because it sounds exactly like what I need to do, offer free shipping on orders over $100 for everything except a few items.
Do you know what am I doing wrong? Any help would be appreciated.
Thanks.
Hi Pam,
You didn’t happen to copy the opening php tag as well did you? That would be the
on the first line.
I put that in my snippets because it highlights the syntax but if you copy that into a part of your functions file where the php is already open then you'll get a syntax error. Let me know if that helps you! :)
I sure did LOL, sorry I’m not very savy on this… Thank you so much for taking the time to reply and specially for posting the code 🙂 I looked everywhere for a way to get this to work the way I need it and was just about to lose my mind when I found this page… I will give it a try tomorrow, this time the proper way. Thanks again!
Worked Perfectly! Thanks Patrick 🙂 you were a life saver.
Great snippet – thank you Patrick.
I cannot seem to get this to work. It still gives me the option of choosing free shipping or standard shipping when in the cart. My client wants to provide gifts with free shipping, while everything else has a flat rate. But, if I have a gift card and other merch together, it gives the buyer the option of flat or free shipping. What I really need is for them to only see the flat shipping price at that point. Any help?
I just tested this snippet on my local install and it works fine for me. There’s a couple things you can check into:
$ineligible
array?Other than those you may need to get a developer to look at it because the code works for me.
[…] Want some WooCommerce hints? Remi Corson posted a neat tutorial on creating a shortcode to display product categories, and Patrick Rauland wrote a how-to for removing free shipping for certain items. […]
Would I be able to change $ineligible to $eligible to enable free shipping on only those product id’s listed? Looking for a work around to the Fedex Shipping Method nightmare.
Hey there, no you can’t just change
$ineligible
to$eligible
. You’d have a write a slightly different filter. That’s a good idea for a blog post for the future.i also want to do what eeBs is doing. To give freeshipping to certain products, so just an opposite to the example you’ve given above. Is there a way to do this?
Hello, I have a rule like this. For orders over 100$ I want to offer free shipping. Certain products tough, which are discounted, are not eligible for free shipping. Your snippet works just fine, but it makes the whole order ineligible for free shipping, even if the order contains 1 ineligible product and 100$ worth of eligible products. Is there any way to modify the code, to offer free shipping for all the orders that have 100$ worth of eligible products(the ones not listed in the array)?
Hello Nicu – I’m sure that’s possible but it would be far more in depth. If I had the time I’d love to look into it but I don’t have the time right now. Do let us know if you end up making that. 🙂
Did you ever have an chance to look into this? I would pay top dollar for this functionality!
Hi Patrick,
Great code snippet. Of course I want to be different as I always seem to do. I have a need to enable Free Shipping on a per Product Basis in WooCommerce. But I am not a coder, can you help or point me in the right direction please.
Hey Ian – it seems like a number of people are asking for this but I don’t have that solution right now. I would reach out to an agency like Codeable to help you with this.
Brilliant and thanks for the pointer.
Hi, Great little snippet that works well however, most of my products are free shipping apart from 3 small items that I will charge postage, so your snippet works for that. But the customer may purchase a large product AND a small item in which case the two would be packed together and the postage would be free.
How would handle that??
You need to add the list of the “3 small products” to the snippet. Then free shipping will be disabled when those products are in the cart and WooCommerce will list of your other available shipping methods.
Hi Patrick
Thanks for your reply. If I add the list of the “3 small products” to the snippet, free shipping is disabled when any of those products are in the cart. That’s great. However I need to disable the postage charge if any of those small (Postage chargeable) products are in the cart WITH a larger FREE shipping product as they will be packed together. Any ideas how that would work?
Regards
Richard
So there’s a particular product that if it’s included always use free shipping. Even if it’s already been disabled earlier by other products? That would have to be another snippet at a higher priority than this one. If you write a function that goes off at priority
30
that would work.Will this code work with flat rate shipping too? If so what part of the code do I change?
No, this won’t work with Flat Rate shipping. That will use a different filter. I believe the filter you would have to use is
woocommerce_shipping_flat_rate_shipping_is_available
. You should reach out to a developer to write this for you.Actually it turns out that the filter is this:
woocommerce_shipping_flat_rate_is_available
just needed to remove the trailing shipping after flat_rate
Hi Patrick,
Thanks for the snippet. I was wondering if this was possible to do based on attribute – as in to disallow pickup based on the item’s attribute?
Thanks.
Hey Jordan, that’s certainly possible but you’d have to add a bit of extra logic to check the attribute. This is actually a good idea for another snippet at some point in the future. 🙂
Patrick,
Thanks for sharing this awesome snippet! Is there anyway that it could be set up to look at the product category instead of the product id? This would make my shop alot more dummy proof for my admin in the long run.
Thanks again!
Hey Christina – that actually should be pretty easy to do. You’ll probably want to use get_the_category. If you aren’t a coder your self I would use a service like Codeable to help you out with this.
Thanks! It´s exactly what I need. I configure it like a plugin, instead paste the code in functions.php, and it seems works fine 😉 I´ll just wait for the book
Hey you’re not supposed to know about that yet! 😉
I’m setting up pages on my site this weekend and the announcement will be Monday morning. Check back then for the blog post. 🙂
Hi,
Can you give me a solution how to disable free shipping for a certain role of logged in users?
It would be best to reach out to a service like Codeable for help with this.
Hi Patrick. Thanks for this. Is this code working nowadays (with all thoses updates WooCommerce had)?
Thanks
Hey Luis – it should still be working yes. Give it a whirl!
Hi Patrick,
Thank you for this! I have gotten your code to work with one of our products. However, when I empty the cart and add a second product to my cart (where I also added its id to the php file as outlined above) the second item show free shipping. I have double checked and I can’t seem to get it to work with more than one product. Any suggestions would be appreciated. I have WC free shipping set to Minimum Order Amount of $20
Well hello there fellow Patrick! I just tested this on my own site and it works fine. Most likely there’s an ID wrong, or some other setting messing with this. Try disabling all other plugins and see if one of them is overriding this functionality.
Hi Patrick,
Your snippet is awesome. Could you help me with adapting your snippet to check for a specific attribute of an individual product – as in to disallow free shipping based on the item’s attribute?
Justin
Hey Justin – glad you like it! sadly I’m quite busy at the moment writing my book and working for WooThemes! I would suggest you reach out to Codeable for creating snippets like this.
I hope that helps! 🙂
Hi Patrick!
I was wondering if you could help me out please. I have 2 different type of photography prints for purchase: Fine Art and Standard. So when you click on an image, the customer has the choice, as I didn’t want to set up another shop page. With Fine Art prints ordered I offer free shipping. On the Standard prints I want to enable shipping costs (Domestic / International), not free shipping.
Obviously I don’t want to put in every print #, but instead somehow cancel out free shipping when a shipping class is selected. Make sense? What snippet of code would I need? Or am I doing it all totally wrong? Which could very well be the case!! lol
Thanks!
Hey CM,
You’d have to write your own snippet that would instead check for a shipping class rather than a product ID. A developer should be able to write that in a snap. I’ve found Codeable is great for these types of tasks. I just interviewed their CEO – it’s worth a listen so you understand how awesome they are at customizations like this.
I hope that helps! 🙂
Hi Pat is there a way to use this code for the Local Pick Up Option instead of the Free Shipping option. I have products that I have the ability for my local customer to pick them up but I have one product that I drop ship so that one product I cannot offer local pick up. Can you help me with this?
Hi Mike,
It’s definitely possible. You try using this filter instead of the free shipping filter:
woocommerce_shipping_local_pickup_is_available
. If you can’t get that to work then you should reach out to an agency like Codeable that specialize in this type of work.Hi Patrick.
It kind of works for me, but only if I have one product (a ‘free shipping’ excluded one) in the cart. If I have two products in the cart, for example like below:
Product A (id added to the snippet, so excluded from free shipping)
Product B (not excluded)
Product A does indeed have the free shipping option removed, but so does Product B i.e. it shows the normal shipping options, in my case Royal Mail.
So it looks like Free Shipping is being removed from both, even though only one of the two products in the cart have had their id added to the snippet.
Any suggestions would be very much appreciated.
Scratch that!
I think it was a cache issue. Everything is working as it should.
Sorry for wasting your time.
Hi David – glad you figured it out. 🙂
[…] On the other hand, if you’d like to learn how to disable free shipping on certain products, please check out Patrick’s detailed post about it. […]
Thanks for the snippet, I inserted it and it worked fine. Then I decided to remove the code and take the hit on postage for a while. However, after deleting the code, every time I try to make an update or save in my wordpress back end, it goes to a blank white page. I also get this when Im trying to log into WP admin page. Can you help with this issue at all?
Hey Simon. Sounds like a pretty big pain. A white screen usually means a PHP error. You could have removed the code incorrectly. Can you download your theme and replace the old functions.php with the new copy?
If you still have problems something else is going on. I would enable
WP_DEBUG
which should then print out an error message on the white page which will help you troubleshoot further.I hope that helps! 🙂
I don’t know whether this would be relevant or not, but i am using the Per Product Shipping extension by WooThemes.
I am using WC Vendors as well, on my website, and to make it easy, its best the vendors assign shipping on a product basis.
I would like a situation where if the Per Product Shipping has been enabled for a specific product, all other shipping options including the Free Shipping is disabled and hidden for that specific item only.
Hey Christopher,
If you want to have some custom logic that overrides the default shipping settings you can definitely do that with custom code. I’d reach out to Codeable (aff).
I try your snippet it only work for me if I only have one item in the cart.
When I have more then one the free shipping option is available.
Any idea…
It should definitely work with multiple products. It looks for a single product that’s ineligible and then it disables free shipping. Make sure one of the products in the cart is ineligible. And as always make sure you’re on the most recent version of WooCommerce.
In the same vein: http://www.remicorson.com/woocommerce-free-shipping-on-a-per-product-basis-in-the-same-cart/
Hi there, Great Post!!
Although still having the “Free shipping” method along with the New “Flat rate shipping” on my particular item and now when I am choosing one of them, it calculates the “Flat rate” like it supposed to.
Is there any way to remove the option of “Free shipping” from the cart specifically on my items with “Flat rate shipping”? (Although eventually it calculates the correct shipping fee and make the “Free shipping” option to disappear)
Hey Asaf,
The free shipping option shouldn’t show up at all. Sounds like something might be cached on your site. I’d disable any caching plugins you have running. Caching with e-commerce is tricky. Companies like WPEngine has caching built in and they work directly with WooThemes when there’s a problem.
Thank you very much!
Very appreciated!
having the same problem “Free shipping” won’t go away
Would it also be possible to go the other way around?
I would really like to offer people local delivery for free but only with our most populair items.
Can i show local delivery ONLY with certain product id’s?
Any help would be much appreciated!
I´m looking for the same thing since months. Can´t find it anywhere
Update: I thought it would be something like this but I cannot figure out the correct variable for local shipping
function my_free_shipping( $is_available ) {
global $woocommerce;
// set the product ids that are ineligible
$ineligible = array( '1194', '1091' );
// get cart contents
$cart_items = $woocommerce->cart->get_cart();
// loop through the items looking for one in the ineligible array
foreach ( $cart_items as $key => $item ) {
if( in_array( $item['product_id'], $ineligible ) ) {
return false;
}
}
// nothing found return the default value
return $is_available;
}
add_filter( 'woocommerce_shipping_local_delivery_is_available', 'my_free_shipping', 20 );
Woeha! sorry for spamming but this actually works:) Maybe useful for everybody else.
This no longer seems to work in the latest WooCommerce version 2.4.6 (WP 4.3.) Is it just me?
Nope, not working on my end either.
Are you able to disable Flat Rate Shipping using this per product basis. I have a customer using UPS for 1 product and Flat Rate for the others. So it shows Flat Rate Shipping as Free on the Product using UPS. Thanks
This Works great, thanks.
What can be done to make free shipping disable on all the products which have a shipping cost. We need to some products which have shipping and some which do no have shipping cost. when both free and flat rate classes are enabled and someone tries to buy a product it gives option for free shipping, how can I disable that option if shipping cost is on the product. We are using wc-vendors plugin so some vendors have shipping on some product and not on other products.
Can we add variable product to this? I mean my product code is 573 but the variations that need to be ineligible for free shipping are #2200 and #2201
As one product has two kinds of variations and they are being sent with no shipping cost and with shipping cost respectively.
How to do this?
It’s possible but you’ll have to modify this code to work with variable products instead of simple products. I recommend Codeable.
Still works today.
Thanks for the resource.
Hi there,
I tried the pasting the snippet into my functions.php file, but got an error straight away, and had to login via FTP to change it back.
Any idea what I did wrong?
I wasn’t sure exactly where to put it in the file, so I just put it at the bottom under everything else. And didn’t have the <?php in there, just the rest.
I'm using Divi Theme v. 2.5.9, and I do have variable products installed but the products i need free shipping for are just simple products.
Any help would amazing, thank you!!
This is great but is it possible to somehow still use a free shipping coupon?
Hi Patrick
Worked a treat thank you.
Cheers
Jeff
Hi Patrick
This was exactly what I had been looking for, had tried many other snippets but none of them worked. Thanks. Great and easy to use.
Louisa
Hi,
I need help! After i copy the snipet into my function.php file, my site went down immediately.
How to restore it?
Thanks
You didn’t copy the opening PHP tag dig you? That’s this bit of code
. I have to add those to the snippets so it shows code highlighting but your functions.php file already has an opening tag and could break if there's a second.
Remove that opening PHP tag in the functions.php file and upload it to your site.
I hope that helps! :)
Is it possible to disable free shipping for products with certain shipping class and orders containing any of these products? Let’s say “bulky products”?
I have this same question
Also forgot to add, with new shipping zones, I am not sure if this snippet is working for me anymore. Any help?
Hi Patrick,
I want some of my customers to book my products without paying for them. So if they click on add to cart button, they will not see the cart page or the checkout page but the product will be booked (directly book without paying).
Thank you in advance
Hello Patrick,
Thanks for this code!
Since my client needs to add the product IDs as they are created (and I don’t want him touching the functions.php file), I have created a simple plugin:
Folder: my-plugin
> my-plugin.php
and pasted your code in here. However it doesn’t seem to work once it’s not in the functions.php itself. Can the add_filter be read from a the plugin folder? Is there a path I need to change to get it to work?
Hi Patrick,
Thanks for putting this together. Could you please tell us, is this snippet compatible with the new Shipping Zones that were implemented?
Thanks!
seems like: woocommerce_shipping_free_shipping_is_available is depreciated ?
Have spent a week trying to understand and implement this very thing (I have one product that should NOT qualify for free shipping).
But it’s Dec 2018 now, and we’re now on Woocommerce 3.5.2 Before I muck up my site, can you tell me if this ingenious solution still works?
Good wishes in any case. Pat
You asked: “If you offer free shipping why would also offer paid shipping? Especially if it’s the same shipping provider”.
Plenty of Reasons. What if you have an item that weighs 100 lbs and don’t want to offer free shipping on this item?
Kind of short sighted, don’t you think?