WordPress nginx update problem

So, this post is probably just a reminder for myself. For years now, I had this stupid problem when I wanted to update a plugin or theme on my wordpress site. On my VPS, I also have a cronjob running which takes care of most of the plugin updates automatically. With a bash script I loop through all my wordpress folders on the server, and then use wp-cli to update the stuff I needed.

I always assumed that this mechanism screw up the ability to update by the browser. Maybe file permissions were overwritten, maybe ownership. I always though: I have to look into this some day. Of course, each time I login to my wordpress and I see pending updates, I still try if it maybe magically works this time. Then the sad smiley face shows up, and I have to remove my .maintenance file manually. Very annoying.

Today I found out that when I wanted to browse the detail of the button, this smiley face also turned up. I finally check my console in the browser, and googled the error that was displayed there.

Then I found out about the X-Frame-Options that were indeed set to DENY in my nginx config (in /etc/nginx/snippets/ssl-params.conf).

I commented this line, and now the I-Frame with the plugin details is working again. I expect that this also solves my problem of not being able to update.


Auto update wordpress plugins using WP-CLI

After installing Wordfence to all of my wordpress sites, I receive a dozen e-mails a day about plugins that should be updated. I don’t use many plugins, so usually I just go ahead and update them with wp-cli, because I feel they are safe to update. Main advantage of the wp-cli is that I don’t get http timeouts or stuff like that.

Updating all the plugins for multiple sites (I think I have about 10 of them) did become a repetitive task, so I wrote a little shell script for it. Of course you have to make sure that all the websites can be auto updated, or if there may be a plugin that will break after an update. So only use this if you are certain you just want to go ahead and take a little risk of breaking things.

I added an .auto-update file in each document root of the site I want to update automatically. I changed the owner permissions of .auto-update to root:webuser so that the webuser cannot remove this file, but still I can use this file to determine the owner of the wp-content folders. I need this to change the ownership back to the website owner after updating.

Very simple and easy script, but well, the stackoverflow generation (myself included) just loves to copy ans paste right? ;-)

I put this script in my /root folder with 700 permissions. I didn’t put this in a cronjob, I keep monitoring the e-mails that Wordfence sends me to see if I need to take action.

Clean infected PHP files with sed on linux console

So I had one of my wordpress site infected. All php files were injected with bogus commands. For example, it looked like this

Infected PHP

So now what? I kind of panicked and deactivated all of my sites. Fortunately I could restore most of them because I had clean backups. For some I didn’t (probably the site which was hacked first).

The bogus all looks the same though, so it should be easy to clean this up right? When the panic was over I took some more time to get this job done on the console.

Eventually I came up with this command:

find ./ -name '*.php' -exec sed -i 's/<?php $am.*-1; ?>//' {} \;

I still don’t completely understand what’s going on, because when I try the regexp at regexr I should escape characters like ?. But well, this seems to work for this particular string. You can change it a bit to your needs. It should start with the very first characters of the bogus, $am in my case, then it uses .* to catch all characters in between, and then -1; is the end of the bogus string.

Maybe some day I may need more complex regular expressions, but for now I wanted to make sure that I documented this.

Update: As bob states rightfully in the comments; this is not a method to completely scan all of your PHP files on the server and find all backdoors. This is just a way to remove bogus code that you already identified. The signature changes with every hack, and also other techniques may have been used / planted to create a backdoor.

So this is not meant as a virus scanner command, but just a hint to show how simple injections could be removed.

WP All Import: calculate discounts using your own function

Using WP All Import, I wanted to use a discount on our prices, but do the math myself. This way I could round the discounts by x.95 or x.45 instead of other weird prices.

The manual suggests you put your function in the functions.php of your theme. But I don’t want the function to be gone when updating the theme, and neither want to create a child theme just to put this one stupid function in. So I thought I’d create a plugin.

This seems to work perfectly. Just put in this code in your /wp-contents/plugins directory. Save this code to, for example, wp-all-import-discount.php.

Now you can easily use it in your import config:

WP All Import discountAs second parameter you can use the discount you want. Use 0.9 for 90%, 0.5 for 50%, etc.


WP All Import cron from CLI (update)

My previous post about executing the WP All Import cron jobs with PHP cli didn’t work too well. Apparently the processing part still stops executing after a while. I ended up with import jobs stating “last activity 5 hours ago”.

Still I don’t want to run this through the webserver. So I wrote a little wrapper shell script to execute my imports in cron. It checks if the line “is not triggered” was partially returned. The wp cron script would return “<p>Import #26 is not triggered. Request skipped.</p>” if you execute the processing action without triggering it first. In other words: the import is really complete now.

I put the shell script outside my document root, and it uses the “logs” directory to store the log files. So make sure this directory is writable and points to the right path.

Now just put this in your /etc/cron.d/yoursite:


WooCommerce: remove empty SKU products

At work, we got ourselves a nice WooCommerce shop. Using WP All Import, it automagically imports the products and updates stock. But somehow in we ended up with products in our database with no SKU.

Removing one of those products, and then rerun the import, seemed to work. So maybe these products where in the product feed once without a SKU. Who knows. Anyways, I wanted them gone, and didn’t want to delete the entire database, afraid of what Google may think of that.

So I checked the database a bit and came up with a SQL query. First, to check if you have products with empty SKU’s as well, you could run something like this:

To remove the products, I think this should do. Of course this wouldn’t delete the files in your upload folder. If you want to delete those as well, you might want to write a script that selects the records, and then removes the images as well.


WP All Import cron from CLI

The WP All Import plugin can be used to import data from xml feeds. We use it in combination with WooCommerce. To automatically update your products and/or stock information, you should run this from a cron job. Unfortunately WP All Import didn’t do a very nice job on integrating this with the WordPress cron system, because I feel that the scheduling should be manager from your WordPress install instead of cronjob settings on the server.

WP All Import suggests that you set a cron job where you request an URL with wget. They already noticed a possible issue with this approach, which is timeouts. Therefore they created two actions: one to trigger the import, and a second one to process the import. When a timeout occurs, the process action should continue where it left of.

In our situation (nginx + php-fpm) this didn’t seem to work very well. The script was aborted after 30 seconds (which is what we have configured on the server ourselves), but when I re-requested the processing action, it said I should run the trigger action first. So, did the processing complete or not?

Since I don’t like running web requests from cron anyways, I searched for a method to run the import with PHP cli. This way we don’t have timeouts and the script will execute nicely. Also you can skip the “The processing URL should be run every two minutes because it may not finish your import in one run.” because it’s very unlikely that the script will timeout.

Enough chichat, here’s the line I added to /etc/cron.d/mywebshop:

Don’t forget to change the path to your WordPress install, and the import_key.