Why WP-Cron sucks

In WordPress you have a small file called wp-cron.php, which simulates normal cron-jobs. except this file isn’t executed every x amount of minutes. Instead WP-Cron will execute when someone visit the website.

The reason why the wp-cron was build, was because some of the actions it does, can take longer than we want the user to wait, so it will run as a separate process in the background. This is good because it means that it won’t affect the user, in most cases.

So here’s why WP-Cron sucks.

If you have a high traffic site, and the WP-Cron is executed on every visit, this might cause multiple processes that is getting started, to run the cron. WordPress is smart enough, to make a lock on the cron itself, so multiple crons won’t loop over the same events, which really helps a lot.

But there is quite a few plugins, that hooks into the WP-Cron, this results in that those plugins will do their thing for every visit. This is usually fast, it takes maybe 100-500ms to execute the full cron, and doesn’t really matter. But lets say you’re having a high traffic site, this means you can get a lot of visits, which means it will spawn multiple crons, in the beginning it seems fine, but the more visits you get, the more processes will get started, this takes CPU time, and memory of the server. The more scripts you put in the ‘queue’ for the CPU, to do the calculations, will result in that the crons start taking longer and longer time. And might end up taking multiple seconds.

A small example was a site, getting around 200 visits within a minute, this started around 90 wp-crons. It started to fill up in the queue for the CPU, which resulted in slower processing of the wp-cron.php. Not only did it become slower, but it also resulted in 150 PHP processes running in the background for that specific site. This took some CPU, but especially memory was used, because each process was taking 18 megabyte of memory, due to some hooks in the WP-Cron. So that minute, the memory usage for that specific site increased 2700 megabyte.

There was a PHP memory limit of 256 megabyte, but this memory limit is for each php process running, and since the WP-Cron starts another process, this means that it doesn’t hit the limit at all.

Since the server had a lot of memory it didn’t really matter at all, but let’s say a few sites on a server is running with this amount of memory, it will eat the memory pretty fast. There’s systems like CloudLinux that fixes this problem, because you allocate x amount of memory to each user, and when the user reaches this limit, it won’t use more. But this means if it’s executing something, it will become slower for the end user, because it’s not able to use more memory, so it will save the server indeed. But might slow down the site a lot.

Another way to fix this, is to disable the cron in the wp-config.php

define('DISABLE_WP_CRON', true);

The problem with disabling the cron is, that some features of WordPress will stop working, things like scheduled posts doesn’t work, because this is depending on the cron itself, and  also other plugins that is hooked into the wp-cron will stop working.

So a way to fix this for a high traffic site, is to disable the cron with the code above, and set up a cron manually in your control panel. This can be done like this:

*/5 * * * * wget -q -O - "http://mydomain.com/wp-cron.php" > /dev/null 2>&1

#Sometimes it might be required to run PHP directly:
*/5 * * * * php /home/$USER/public_html/wp-cron.php

#You can also do it using curl:
*/5 * * * * curl -vs -o /dev/null http://mydomain.com/wp-cron.php > /dev/null 2>&1

The code above will run the cron for every 5 minutes, and redirect all output to /dev/null.
Running it every 5 minutes means it will run 288 times every day. Which should be fairly enough. You can even set it to every 10, 15, or 30 minutes. Based on what you prefer.

So setting the cron to a run every x minutes, will first of all lower the load on the server, and maybe even make your site load faster if you had problems, with slow loading times, when you have a lot of visitors coming in.

Multisite

If you have a wordpress multisite, and want to do the above, you have multiple ways of doing it.

Lets say you have a wordpress multisite with following domains:
1: http://domain1.com/
2: http://domain2.com/
3: http://domain2.com/blog/

Solution 1

What you can do, is using the method above, by disabling the wp-cron just the way you do for a normal wordpress site.
Then you can set up 3 cron jobs – all 3 using the command above, but replacing the domain. This means you will have 3 cron jobs running doing their own wp-cron.php

Solution 2

This sucks if you have, lets say 100 sites, so there is another solution for this:

Creata a file in / called wp-cron-multisite.php:

<?php
require('./wp-load.php');
global $wpdb;
$sql = $wpdb->prepare("SELECT domain, path FROM $wpdb->blogs WHERE archived='0' AND deleted ='0' LIMIT 0,300", '');

$blogs = $wpdb->get_results($sql);

foreach($blogs as $blog) {
    $command = "http://" . $blog->domain . ($blog->path ? $blog->path : '/') . 'wp-cron.php';
    $ch = curl_init($command);
    $rc = curl_setopt($ch, CURLOPT_RETURNTRANSFER, FALSE);
    $rc = curl_exec($ch);
    curl_close($ch);
}
?>

What you then can do, is to use the command:

*/5 * * * * wget -q -O - "http://mydomain.com/wp-cron-multisite.php" > /dev/null 2>&1

Whats nice about solution 1 is that you’ll be able to set a frequency for each site you have in your network. So if you have a site that only needs to be updated every hour, but another needs to be updated every 5 minutes. Then you should go for solution 1.

But if everything just should update at same time, and same frequency, and that you have a lot of sites, then solution 2 should be the easiest one.

Update 1:

I found out that there was a bug in the cron when using ‘date +%s’ – this isn’t supported in cron jobs, so the % needs to be escaped  like this:  ‘date +\%s’. The code blocks is updated.

Update 2:

Thanks to Johann for finding errors in the multi-site script! Code above is corrected 🙂

 

  • Nice post!

    Thanks, I just disabled the cron completely, I wasn’t using it anyway 😛

  • TheWorldsBestPies
  • Any idea how to do this for a Multisite? WP-Cron is going crazy on me, but on Multisite running the main wp-cron file doesn’t cut it.

    • lucasrolff

      I’ll update the post to answer the multisite question, take a look in 20 minutes or so.

  • Oudin

    How can I test and troubleshoot to see if this is work and what might be causing it not too

    • lucasrolff

      What you can do, is either to download the access logs, for your site in your control panel, and search for wp-cron.php, if it shows up, it is working.

      Another one, is to try setting up a post that should be published in 5 minutes etc – if your wp-cron is set to run every 5 minutes, then if you check efter 5-10 mins or so you should see the post published. Then you know it’s working as well.

      • Oudin

        thanks

  • Oudin

    WIll the script only run for a max of 300 sites as set by the LIMIT?

    What if you have a 1000+ would the script be able to support it?

    • lucasrolff

      The limit set for 300 is just an example, you can put even 2000 if you want, or 10000.

  • lucasrolff

    The post is updated. +%s needs to be escaped when using cron jobs. See the code blocks above for the updated line.

  • Johann

    Some small errors in your code. Just in case somebody has trouble seeing them:

    requre(‘wp-load.php’);
    to
    require(‘./wp-load.php’);

    $sql = $wpdb->prepare(“SELECT domain, path, FROM ept_blogs LIMIT 0,300”);
    to
    $sql = $wpdb->prepare(“SELECT domain, path FROM $wpdb->blogs LIMIT 0,300”);

    • lucasrolff

      The code above has been updated! Thanks for finding it! 🙂

      • Johann

        You still missed some 🙂

        requre
        to
        require

        path,
        to
        path

        Thanks for the Code anyway 🙂

        • lucasrolff

          Fixed 😛

      • Don

        You didn’t update the code, you still have that extra “path,” comma.

        • lucasrolff

          Should be done now

        • Don

          thanks! you just fixed it!

  • Thank you for this article. It helps me much.

    • lucasrolff

      Please let me know if there’s other stuff I should write about, related to wordpress or something else.

  • Jimmy

    I can setup the cron so it runs but it doesnt seem to trigger my schedule posts.

    Has anyone test this on multisite ?

    • lucasrolff

      Hi Jummy, the code should work, what it does, is to connect to the database, get the list of blogs, and simply make a for loop that does a curl for each page.
      Have you checked if the pages are actually getting curled (You can do it by checking access logs in your control panel at your webhost)

  • coder

    i modified one plugin cron that depends on wp-cron to run every 1 min.
    in debug i can see its running every 2 hours.
    1. disabled wp-cron in config
    2. enabled 6 cron jobs in godaddy
    dose anyone please know whay its not executing
    thank you
    wordpress

    • lucasrolff

      Can you try take a screenshot of the cronjobs you did put in godaddy control panel, when I can maybe spot the issue.

  • Víctor Gómez

    A little change in line 5 that return error in WP 3.6:
    $sql = $wpdb->prepare(“SELECT domain, path FROM $wpdb->blogs LIMIT 0,300″, ”);

    • lucasrolff

      Should be fixed in a few minutes, cache for the page is getting cleared.

  • Brooke

    Excellent Lucus! Thank you so much for this post! I am running on WordPress Multisite & am thinking of implementing a combination of Solution #1 & Solution #2. I have 1 question for you before I do so. I understand if I implement Solution #2 and set it to run every 12 hours that it blankets the entire Multisite network of let’s say 100 websites. Can I then add more cron jobs to run more often (maybe every 15 minutes – implementing Solution #1) for only 3 of those sites? That way I would not have to use Solution #1 and add a cron job for every single site. Instead, I would have 1 cron job that runs for each of the 100 sites every 12 hours & then 3 additional cron jobs that run every 15 minutes for a grand total of 4 cron jobs. Does that make sense?

    • lucasrolff

      Hello Brooke,

      you can surely mix solution #1 and #2 – and there would be no problem adding 3 additional crons running every 15 minutes, every 12 hours the crons will run at same time, but in the end it doesn’t really matter – because wordpress will see that a cron is already running and therefor not run the job. So it will work just perfect!

      Please let me know if you have any other questions.

      • Brooke

        I just thought of 1 more thing too! Do I have to worry about wp-cron-multisite.php file being deleted on my next WordPress Update?

        • lucasrolff

          It will not be deleted on update. It will be kept during upgrades 🙂

          • Brooke

            I’ve seen some variations in other posts. Why do you prefer to wrap with quotations and append ?doing_wp_cron=`date +%s` to the end?

            Is there a benefit to doing that rather than:
            wget -q -O – http://mydomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

          • Brooke

            the date part

          • lucasrolff

            The reason why I’ve decided to append the date, is to make an exact match how wordpress works internally, in general this is to make sure that the cron will always be run, because some hosts, use a caching system, that might cache the results, so you force it to run by appending the date.

  • Nick

    Thanks mate

    Going to give this a whirl.

    Does curl need to be enabled in the apache php / httpd config I assume? Or is this included in a normal php build?

    • lucasrolff

      Since you’re executing it as a cronjob, which is a system process, it shouldn’t require apache or even httpd to support curl (they likely do anyway). So it should work with ANY web hosting that runs linux at least.

  • Thank you so much for this Lucus and sunny greetings from Diani Beach! You are my hero! I have recently found this errors in web root:

    [05-Oct-2013 22:00:49] PHP Warning: require(./wp-blog-header.php) [function.require]: failed to open stream:
    No such file or directory in /home2/damian/public_html/index.php on line 17

    [05-Oct-2013 22:00:49] PHP Fatal error: require() [function.require]: Failed opening required
    ‘./wp-blog-header.php’ (include_path=’.:/usr/lib/php:/usr/local/lib/php’) in /home2/damian/public_html/index.php
    on line 17

    Funny enough that when I contacted hostgator about these sometimes 20 errors a day I was told that is due to my outlook and I have spent hours by fiddling around the outlook settings. I am very happy to find this article!I have just noticed this in processes in cpnel, how it got stuck there 🙁

    So I understand I need to disable it in wp config php but I have no clue what you mean to set up a cron manually in your control panel. Where? Can you please point out how to do this for such beginners as me?
    Thank you so much for your time!

    • lucasrolff

      Hello Villas Diani ! Thank you very much for writing a comment on my blog.

      The error you’re receiving above, has nothing to do with outlook.

      What the error says is that in your index.php file for WordPress, on line 17, it tries to load the file “./wp-blog-header.php” – this file, loads a lot of information that WordPress needs, to be able to function.

      This is usually located in same folder as the index.php file (you can see it when it loads ./) – ./ means “current folder”.

      It seems like this file is missing, it can either be due to the file is located elsewhere (Would surprise me), or the file somehow was deleted.

      Since the file is the same on very WordPress installation, you can navigate here: http://droplr.lucasrolff.com/sWlY/3XlyDigL download and open the zip file, this contains the php file, that you can upload to the public_html folder within FTP or your file manager in cpanel. This should fix this error.

      If you want to set up the cron manually, for cpanel you can follow this guide: http://www.youtube.com/watch?v=ZfdRDM5qVGM

      I hope this will solve the issue you’re facing!

      • Martina

        Thank you so much Lucas for your reply and all the information. Thank you very much for your easy understand video tutorial, even me-absolute beginner could set the cron in no time, thank you for this!

        As regarding of the php error which is trying to get me on a medication over the last few days I have still not found a solution. I have followed the steps, uploaded the wp-blog-header to public_html and a new error appeared instead of the previous. In the new error it wants again something on line 12 for a change:-(

        [13-Oct-2013 22:26:01] PHP Warning: require_once(/home2/damian/public_html/wp-load.php) [function.require-once]: failed to open stream: No such file or directory in /home2/damian/public_html/wp-blog-header.php on line 12
        [13-Oct-2013 22:26:01] PHP Fatal error: require_once() [function.require]: Failed opening required ‘/home2/damian/public_html/wp-load.php’ (include_path=’.:/usr/lib/php:/usr/local/lib/php’) in /home2/damian/public_html/wp-blog-header.php on line 12
        [13-Oct-2013 22:26:01] PHP Warning: require_once(/home2/damian/public_html/wp-load.php) [function.require-once]: failed to open stream: No such file or directory in /home2/damian/public_html/wp-blog-header.php on line 12

        When I view wht is on line 12 i see there this code: require_once( dirname(__FILE__) . ‘/wp-load.php’ );

        I am really frustrated of this:-(

        • lucasrolff

          I’m sad to inform that it seems like your whole WordPress installation is broken, due to a lot of files missing, you might want to ask either the hosting company you use, if they can help you out, or a friend if you know someone that knows WordPress quite well.
          They should be able to help you restoring the default WordPress files.

          • That is what I was afraid of:-( Too many people were digging into this site. Anyway, thank you so much Lucas for your time.I have found on your site some great post which I believe will help me to improve my knowledge in wordpress.

  • todd

    If I’ve got 100 sites on my multisite network and set it to run once a day for example, is wp-cron.php going to run on all 100 sites all at once? or will they run sequentially one after another? thanks

    • lucasrolff

      It will make a for loop, that means that there will be a small delay between every site, but if you want, then you can put a sleep(10) in the loop – this is hacky, but will sleep 10 seconds after each curl, resulting in a 10 seconds delay.
      Keep in mind that some hosts, don’t allow long running crons. But try without the sleep first. And try configure the cron to run outside peak hours.

  • Roderick

    Hey, I want to use a third party crons system. What’s the best way to do that? or what would the url look like since that’s all they are asking for?

    • lucasrolff

      If you use a third party cron system, it should work exactly the same way.

  • Mae

    define(‘DISABLE_WP_CRON’, true);
    &
    Use http://www.easycron.com

    • lucasrolff

      Why not use your hosts cron job?

      • Mae

        There are many hosts out there that have limit on cron job feature, and easycron is handy to use.

        • lucasrolff

          Make sense then! And sad some hosts have those limits !

  • HARTmarket

    Greetings! Thanks for this script … it seems to work but is having adverse effect on my sites. I was getting the high CPU usage before, with the cron checking every time there was traffic basically … and hostgator was shutting me down. But – as bad as that was, it was site based … Now, on my multisite with 20+ subdomains, the cPanel based cron job runs periodically – but, for all 20+ sites at once at the same time, which even if run once a day – shuts my server down when it runs.

    Is it possible to be able to create cron jobs that calls this wp-cron-multisite.php and direct it to a specific subdomain of the multisite? Then, I could create 20+ cron jobs and allocate each subdomain say, every 3 hours to lessen the burden.

    • lucasrolff

      The easiest way, but not the most optimal, would be to, copy the file, call it e.g. wp-cron-multisite1.php, 2, 3, 4 – and then change the limit as following:
      limit 0,5
      limit 5,5
      limit 10,5
      limit 15,5

      Set a cron where you change the name of the file

      I’ll post another solution, but will require some more work – I’ll get back to you when I have the solution!

      • HARTmarket

        thanks for speedy response! But, won’t each one -1.php, -2.php etc call all 20+ at the same time? I can give that a try and look forward to you finding the solution 🙂

        • lucasrolff

          It shouldn’t, due to the LIMIT part of the SQL query that is run, what it will do:
          LIMIT 0,5:
          Select the first 5 results from the database

          LIMIT 5,5:
          Select 6,7,8,9,10 result from the database

          LIMIT 10,5:
          Select 11,12,13,14,15 result from the database

          And LIMIT 15,5:
          Select 16,17,18,19,20 result from the database

          • HARTmarket

            Hi .. sorry to bother you again … when you say “change the limit” .. what do you mean? Do you mean inside each of the wp-cron-multisite-1.php etc where you have limit 0,300 ?? or is this something that is done part of the wget command inside cPanel cron job?

          • lucasrolff

            currently where you see the limit 0,300 you should change it to 0,5
            and in wp-cron-multisite-2.php you define 5,5 e.g.

            Like this:
            http://paste.slcoding.com/?751a49b201e49dfe#x3pB8bRxxfyVtYxaA4Uq2ZkCg0gDe2B8kTn/NrUtZ2I=

            (Link is active for 30 days)

  • Arno

    A question: Is the parameter doing_wp_cron=`date +%s`really needed for the multisite script? I can’t see what it is used for.

    • lucasrolff

      Hi Arno,

      You don’t need to put the `date +%s` part there, but in general it’s recommended to make sure that the PHP script is not cached. The post is based on how the current implementation of WordPress which also put the timestamp in the URL.

      Feel free to put whatever query string you want.

      • Greg

        You don’t want anything assigned to doing_wp_cron because it’s value will mismatch the doing_cron transient and wp-cron.php will exit without doing anything (lines 74-76 of wp-cron.php as of 4.1.1)

        • lucasrolff

          There’s nothing wrong putting the timestamp after doing_wp_cron, this is exactly what WordPress does (See attached image).

          If you strace the process executing the wp-cron, you’ll also see it works just fine. (Both for 4.1.1 and previous versions).

          appending `?doing_wp_cron=timestamp` or any other text, will also prevent the server from caching the request since we’re doing a GET request and not a POST.

          Please do an strace of the process, and you’ll see that it works just fine 😉

          • Greg

            When WordPress calls wp-cron.php, it sets the doing_wp_cron value to the same value that it sets the transient to. It can do that because it’s the one setting it. From the OS, you don’t have that knowledge.

            Look at lines 77-79 of wp-cron.php. It bails out because you don’t have the right value of the lock (the transient).

          • lucasrolff

            Again – if you strace the process, you see it still doing what it should do, execute the cron, and do the tasks (Such as publishing scheduled posts, prefetch in wp-super cache etc etc). All these things happen just fine in 4.1.1 and below. But to make you happy, I’ve updated the code examples, to replace the doing_wp_cron with putting the timestamp, to prevent caching.

            But still, the crons is executed just fine, you can verify this by stracing the processes in the OS.

          • Greg

            Setting up a cron job to trip, then stracing the PHP process is a long way to go to see it not work. I’ve put debug statements in wp-cron.php to verify it. It exits on the lines given above. Have you looked at the code? Your apache log doesn’t tell me anything – I see the erroneous cron GET, then a bunch of unrelated GETs.

          • lucasrolff

            Yes I’ve looked at the code, the apache log tells me that the cron is executed, and the indexing from WP Super Cache is started right after (Which is triggered by the cron).

          • Greg

            You’ve looked at wp-cron.php? So how is your wget, with a value set on doing_wp_cron, getting past this lock check?

            // Check lock
            if ( $doing_cron_transient != $doing_wp_cron )
            return;

          • lucasrolff

            Good question, but tell me, how does the cron then trigger if it doesn’t get past this lock check? Because how does WP Super Cache regenerate it’s cache, and scheduled posts get submitted if the cron doesn’t work? Just wondering 🙂

          • lucasrolff

            http://paste.lucasrolff.com/6c776c8074e450fbc2e3d8b7c95d9572adab460a shows the access log indexing just fine based on the cron

          • Greg

            Hi Lucas,

            Just confirmed it for sure with a testcase. ?doing_wp_cron=$(date +%s) and a once-a-minute job would not run if due. Turning that into ?doing_wp_cron and it runs.

            If you’re seeing jobs run, it’s because you’re getting visitors to your site or you have the admin interface open (it does POSTs every few seconds which kick off cron). If you’re assigning anything to doing_wp_cron, then it’s not the curl or wget lines making cron run.

          • lucasrolff

            It will not fire if define(‘DISABLE_WP_CRON’, true); is set 🙂

          • Greg

            DISABLE_WP_CRON is not set.
            About time you admit you’re wrong and edit your article Lucas 🙂

          • lucasrolff

            If you actually read the article, then you’ll see it is set 😉 and also the ticket was updated long time ago with a statement that you believe it’s wrong, so why bitching? I hope you did actually read the thing, which you surely didn’t.

            But hey, it works fine, most posts on this site is scheduled posts, and they get posted on time

            So it works just fine, if you believe it or not, that I don’t really care about

  • txyoji

    I’d recommend adding a filter to the query to ignore disabled or deleted blogs.
    $sql = $wpdb->prepare(“SELECT domain, path FROM $wpdb->blogs WHERE archived = ‘0’ and deleted = 0″, ”);

    • lucasrolff

      Should be added in a few minutes! Thanks for the suggestion

  • vikram singh jodha

    Hi, Thanks for awesome post, i have some different requirement.

    there is multipal sites hosted on my server and they are using the same cron at different interval but i want if site A is running cron process then i don’t want that any other site starts the cron at that time till site A finish, site B can start after A.

    i don’t want simultaneous cron at same time.

    so how can i achive this.
    Thanks in advance

    • lucasrolff

      There’s not really a way you can control this, if you can, you would need to have full control over the server, and use something like http://www.unixwiz.net/tools/lockrun.html

      The easiest would be to just run the crons on different intervals. http://corntab.com/pages/crontab-gui might be helpful to fully decide that intervals you want for your crons.

      • vikram singh jodha

        i got input from someone that told me that i can lock other cron process using memcache or something like this https://github.com/arvenil/ninja-mutex

        but still have confusion so can you provide some input for memcache and is this possible with it.

        Thanks

        • lucasrolff

          I have no such experience with that kind of use for memcached. It’s probably possible, but, thats out of the scope of this post.

          I might look into it one day, to see if there’s a nice way to do these kinds of things.

  • Nathan

    HELP!!! Does anyone have any suggestions?!?!?

    I have a plug-In that is throwing an error code when deactivated: -Warning Illegal offset in wp_includes cron.php file on line 180…..

    …..if ( isset( $cron[ $hook ][ $key ] ) ) {

    It is an IDX plugin for a real estate website. Please help… I believe this is what is bogging down my site, causing it to load sloowww.

    • lucasrolff

      I would contact the plugin owner and ask him to fix it.

  • Ronnie

    Thank you.. I have been struggling with getting Cron working on my webhosts restricted server packages. Had to upgrade just to be able to get a triple events scheduler in cpanel. I still couldn’t make it work! Anyway, I read your instructions today and disabled the internal cron and finally managed to setup dedicated external crons. Hopefully that’ll fire off the jobs on time. Shame wp-cron is so unreliable and unpredictable. Thought this stuff would be easy! If my site http://www.champions-league.today stays current and up to date, then I’ll be happy!

    • lucasrolff

      Sucks to hear your webhost is that restricted! Hope it will work out with the external crons! Else send me an email at lucas @ lucasrolff.com – I’ll be able to sort something out for ya.

  • Ploert

    Hi!

    I’m implementing your multisite solution 2 but keep getting a 404 error.

    The cronjob sends me an email with the error. See below (Note: I replaced my domain with “domain.com”):

    –2014-08-23 11:40:01– http://domain.com/wp-cron-multisite.php?doing_wp_cron
    Resolving domain.com… connected.
    HTTP request sent, awaiting response… 404 Not Found
    2014-08-23 11:40:01 ERROR 404: Not Found.

    And when I visit the script directly in the browser, I also get 404 errors. The document header sends a 404 and the page looks like below, it outputs a 404 errors for every site in the network:

    Not Found
    The requested URL /wp-cron.php was not found on this server.

    Not Found
    The requested URL /nl/wp-cron.php was not found on this server.

    Not Found
    The requested URL /de/wp-cron.php was not found on this server.

    Any idea what i’m doing wrong here?

    • lucasrolff

      Hi Ploert,

      Can you maybe contact me my email lucas @ lucasrolff.com – then I can help you resolve it, with some more information.

      Best regards,
      Lucas R

      • Tamara García

        I have the same problem! Did you found the solution?

        • lucasrolff

          Please send an email, then I can take a look at it.

          • Tamara García

            Hi lucasrolf, I resolved my problem, I had incorrect my virtualhosts. Thx!

          • lucasrolff

            Cool! I’m happy it got solved! Wishing you a nice day

  • Is there a way to “measure” the amount of resources that wp-cron takes up? Also, is there a way to see what processes get triggered by wp-cron? Been having issues with heavy traffic spikes on my site despite having done this.

    Would welcome any suggestions. The site I am referring to is http://astig.ph

    • lucasrolff

      You can use tools like new relic to measure these things or XHProf for further profiling – there’s also plugins that can show which things that will be executed – https://wordpress.org/plugins/wp-crontrol/

      But it can be a rather extensive task to measure these things.

  • littleguy

    You talk about how WP Cron sucks, then you fix all the issues. Nice of you. 🙂

  • ZacharyH83

    I used this, and it does great!

    I have a question though,

    I’m added some functions to wp-cron with the schedule event. These functions are located in classes that are within my theme.

    Will running wp-cron standalone load my classes as well? or do I have to include them separately on the wp-cron file?

    • lucasrolff

      Hi @ZacharyH83,

      If you call wp-cron.php it will run as the normal WP cron, including the functions you’ve added to schedule events in the WP API.

      What the script above does, is to disable how WordPress calls wp-cron.php, instead of doing it for (almost) every pageview, it will do it on a timed way instead, so the functionality of wp-cron.php stays the same, and plugins / custom code that is hooked into wp-cron will continue to work just fine!

      If you experience any issues, please let me know.

  • GeorgeV

    Hi, my server does not allow running wget in cron tasks and i can only use php. That means that i can not use wp-cron.php?doing_wp_cron because it gives file not found and i can only use wp-cron.php. My question is : If use only the command php -f path/wp-cron.php will this run for scheduled tasks or i have to find a way to include the ?doing_wp_cron

    • lucasrolff

      Hi @disqus_Lc19poQyY4:disqus,

      Try not to use the doing_wp_cron, it should work anyway.

  • Hey, thanks for the script! Quick question: what does this part do? > /dev/null 2>&1

    • lucasrolff

      It redirects the output (stderr and stdout) – so any output including possible errors (such as timeouts, it happens sometimes) – to /dev/null, which is like a black hole – it prevents you from receiving a bunch of emails every time.

      Alternatively you can remove the 2>&1 and you’ll receive emails from stderr (errors).

      • Awesome, thanks for the quick reply!

        • lucasrolff

          No probs

  • ‫مصطفی نظرزاده‬‎

    What’s the point in the “date +%s” part? only for it not to be cached? why not use only “t=date”?

  • rcaruk

    I have a multi-site set up using sub folders. I’ve switched WP cron off in favor of creating my own. The problem is if I try to run the crons as mentioned above I get a 404.

    http://www.example.com/site1/wp-cron.php
    http://www.example.com/site2/wp-cron.php
    http://www.example.com/site3/wp-cron.php

    The main domain http://www.example.com/wp-cron.php works fine.
    It’s my sub-sites who’s scheduled post tasks keep getting missed.

    Is there any special trick for handling wp-cron.php for multi site sub folder setups?

  • Subash

    http://www.ibps.in/cwe-po-mt-4/?doing_wp_cron=1444995638.9974210262298583984376

    is it possible to find the time of performing of task from this link?

    I’m not the guy of computer major. So please excuse if my question is wrong.

    • lucasrolff

      Fri Oct 16 11:40:38 UTC 2015 is the time it was ran.

  • Hannah Meunier

    Nice analysis . Just to add my thoughts if people need to fill out a IRS SS-4 , my boss saw a blank document here http://goo.gl/PtA6kA.

  • Reedyseth

    A very well document post. Awesome !

  • nske

    Cheers for the good tip.

    Whenever possible -and it should be in most setups-, it’s ideal to avoid the HTTP/FCGI/WhateverElse stack all-together and run the script directly, locally.

    So instead of hitting the http URL with wget/curl, the first choice should be to run wp-cron.php directly with the php executable, like in your second example.

    This saves some overhead and avoids the possibility of timeouts at the intermediate layers as well as problems with situations where the site cannot be contacted from the inside -common in some reverse-proxy setups.

    • lucasrolff

      True that, but some hosting providers offers cron services only doing curl on URLs, and sometimes it might be simpler for some people to use the URL.

    • Mick

      Could you possibly elaborate on how to set this up please?

      Thank you.

      • Mick

        Just to add, I have tried running the file directly from PHP via cron like so:

        /usr/local/bin/php -q /home/accountname/public_html/wp-cron-multisite.php

        but it doesn’t work for me.

        Thanks again!

  • paran

    I don’t really understand, wp check first that if there is a schedule event need to be run and no any event is running, then run wp-cron.php. so even 1000 users visit my site in a second, shouldn’t it is just run 1000 times of check and run wp-cron.php for 1 time?

    • lucasrolff

      If 1000 users visit you’ll have 1000 useless calls to wp-cron.php for doing a check, since it’s a POST request, it *will* take resources.
      And let’s say you have 1000 visitors, one every second, if your wp-cron.php takes 0.5 seconds to run, you’ll still end up actually executing the wp-cron.php (which does consume a rather big amount of resources whenever running), so you’ll make 1000 WP cron calls for no reason, which might be the difference from you having to change host or upgrade to a larger package.

      If you just disable it, and only execute it every 5 minutes lets say, you could save 600 calls to PHP if your cron takes 0.5 seconds to execute – resources which could be used for something better, such as serving your actual visitors.

      • paran

        Thanks for your reply, but I still confusing. there are two functions in wp core, `wp_cron()` and `spawn_cron()`. on the load of every pages, it check first and make sure that do cron only if there is any event need to be run and no any other crons is running, then send POST request to wp-cron.php which in `spawn_cron`.

        so I thought the flow is:

        1. You and me and others visit a wp site.
        2. you trigger the cron job first at 13:00, then wp send POST request to wp-cron.php, then the cron locked.
        3. my turn, wp checked that there is a cron is running by compare my visit time vs locked time by you + WP_CRON_LOCK_TIMEOUT (60 seconds default),
        4. I will no chance to send POST request to wp-cron.php unless the prev cron job you trigged did not finished until 13:01

        Did i miss something?

        • lucasrolff

          Might be that the WP_CRON_LOCK_TIMEOUT will prevent some overhead, but regardless of that, for every pageview, you’d have to check whenever the lock exists or not – and from experience on very high traffic sites, the lock doesn’t seem to work as intended. The lock was implemented before I wrote this blogpost initially, and at that time it clearly didn’t work. And still doesn’t work as well as it should.

          It’s well known on high traffic sites that wp-cron.php causes *a lot* of issues in terms of performance and can steal plenty of resources, so disabling it and setting up a manual cron has helped in a lot of environments – regardless of whatever checks that happens – the lock happens within a database lookup, so assuming you have thousands of visitors (where I first experienced this, we had sites being busy enough that even saving a MySQL query would be good).

          And regardless of the (already) low timeout, majority of people might not even want to run a cronjob every 1 minute

          We have to keep a few things in mind:
          1: WP Cron (might) execute if there’s visitors on the site, if it’s not running, and if the lock doesn’t exist – but regardless you’re doing lookups in the core that is not required.
          2: Even sites with not much traffic, might want to run crons every 5 minutes, or every 15 minutes depending on various factors, if they do not have much traffic, going for a normal system cron makes sense regardless.
          3: More and more things are hooking into wp-cron, sites are getting more complex these days and (sadly) still bloated with a lot of plugins, many of them which uses wp-cron – you might want to control the frequency of your crons.

          I’ve also seen sites where the triggering of wp-cron was so heavy that it actually affected user browsing (probably due to bloated databases), since by default wp-cron is triggered by your main php process entering the site, it forks off and runs in the background.. But the time it takes for the system to boot and get forked off it can be time consuming, leaving the visitor waiting for additional server-side processing – it’s being very picky regarding performance – true.

          But honestly, wp-cron have always been awful, even after they tried to fix it (which also is proven by the fact this blogpost alone has 150-200 pageviews every single day from legitimate users for more than 3.5 years in a row).

          Regardless of locks etc, it *does* consume resources that can be used better – it does affect performance on high traffic sites, and might affect a percentage of users actually hitting the cron.

          • paran

            Thank you so much for so detailed answers. No doubt that wp-cron is awful, I want to figure out it as I’m developing a plugin which relied cron job. I researched some autoblogging plugins relied cron and try to find a elegant way which is performance cared and DO NOT need users to do extra configuration outside of their wp-admin, seems it’s impossible and best solution is still use wp-cron to achieve automate jobs and tell users to read your post to setup real cron-job.

            However, thanks again:)

  • Mick

    I’ve been using your solution 2 for a short time and it’s been working really well, thank you.

    I am now experimenting with domain mapping that is built into WordPress for multisites ( https://wordpress.org/support/topic/howto-domain-mapping-without-a-plugin-instructions/ ) and it is soo much easier, BUT, it breaks your version 2 solution. I believe this is because the subdomain ceases to exist as an uri?

    Have you any idea of a work around so that both methods can still work together?

    Thanks for any help.

  • Jake

    It’s not just an issue for high-traffic sites. The cron is spawned on page load by an HTTP loopback with a timeout of 1 second, so will add a 1 second delay to page load whenever cron needs to be spawned. The cron will then compete with the page load for database access (and other resources).

    So on a low-traffic site, many visitors will find the first page they request slow to load, if there hasn’t been a visitor for a while, because their visit is spawning the cron.