How to Host any (.js)-File Locally (and keep it updated using Crontab)
I’ve received many requests on the WordPress support forums to create plugins to host certain files locally.
Besides CAOS and OMGF I recently created a plugin which enables you to easily save a local copy of any external script or style, called HELL or: Host Everything LocaL. But since there are still plenty of scenario’s that aren’t covered by CAOS, OMGF or HELL I figured I’d show you how to host any file locally and keep it updated using cronjobs.
Hosting a File Locally and keeping it updated using Crontab
If you came here looking for a way to host Google’s Analytics, Google Fonts or other Scripts/Styles locally, I’ve got a way better way for you. I built three plugins for WordPress to take care of that for you. CAOS takes care of hosting analytics.js locally and keeping it updated. OMGF allows you to easily download Google Fonts and generate a stylesheet for it. HELL allows you to easily fetch any enqueued JavaScript and/or CSS Stylesheet and serve it from a local source.
What we’re going to learn in this How-to, is essentially two things:
- Modify a PHP-script, which will download and update our file;
- Schedule a cronjob, which will trigger the PHP-script at a set time.
To host locally, or not to host locally
If you found this tutorial through Google, you’ve most likely have searched for terms like: host file locally, local host webfont, local host javascript, minimize external requests or minimize DNS requests.
Whatever it was, you’ve probably visited Pagespeed Insights, Pingdom, GT Metrix, or any website performance tool. It suggested you should host files locally, instead of making requests to external hosts and you want to know how to get rid of it.
That’s why I assume you already have an idea of which file you want to host on your own server, which is awesome!
Although I already created a plugin for WordPress which enables you to host Google Analytics locally, I’ll use analytics.js
as an example throughout this post. Because I know it’ll work.
Theoretically it is possible to host any javascript file locally. The only thing I want to say is, and I can’t stress this enough, TEST!
A script can be (poorly) built in a way that it won’t work when it’s hosted from a source other than its origin. That’s why it is very important to test whether your script still produces it’s results as expected after copying it to your server.
Creating the PHP-script
The following script is basically ready to go, the only values for you to adjust are:
$remoteFile
: the source of the scriptfile you want to download,$localFile
: the path to where you want to save the file — including filename,$uploadDir
: the path to where you want to save the file, without the filename.
Edit and upload the script to your server in a convenient location, to keep your crontab readable later on.
<?php | |
// Script to update any js-file | |
// Credits go to: Matthew Horne | |
// Remote file to download | |
$remoteFile = 'https://www.google-analytics.com/analytics.js'; | |
$localFile = '/path/to/your/webroot/analytics.js'; | |
// Check if directory exists, otherwise create it. | |
$uploadDir = '/path/to/your/webroot/'; | |
if (!file_exists($uploadDir)) { | |
wp_mkdir_p($uploadDir); | |
} | |
// Connection time out | |
$connTimeout = 10; | |
$url = parse_url($remoteFile); | |
$host = $url['host']; | |
$path = isset($url['path']) ? $url['path'] : '/'; | |
if (isset($url['query'])) { | |
$path .= '?' . $url['query']; | |
} | |
$port = isset($url['port']) ? $url['port'] : '80'; | |
$fp = @fsockopen($host, $port, $errno, $errstr, $connTimeout ); | |
if(!$fp){ | |
// On connection failure return the cached file (if it exist) | |
if(file_exists($localFile)){ | |
readfile($localFile); | |
} | |
} else { | |
// Send the header information | |
$header = "GET $path HTTP/1.0\r\n"; | |
$header .= "Host: $host\r\n"; | |
$header .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6\r\n"; | |
$header .= "Accept: */*\r\n"; | |
$header .= "Accept-Language: en-us,en;q=0.5\r\n"; | |
$header .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"; | |
$header .= "Keep-Alive: 300\r\n"; | |
$header .= "Connection: keep-alive\r\n"; | |
$header .= "Referer: http://$host\r\n\r\n"; | |
fputs($fp, $header); | |
$response = ''; | |
// Get the response from the remote server | |
while($line = fread($fp, 4096)){ | |
$response .= $line; | |
} | |
// Close the connection | |
fclose( $fp ); | |
// Remove the headers | |
$pos = strpos($response, "\r\n\r\n"); | |
$response = substr($response, $pos + 4); | |
// Return the processed response | |
echo $response; | |
// Save the response to the local file | |
if(!file_exists($localFile)){ | |
// Try to create the file, if doesn't exist | |
fopen($localFile, 'w'); | |
} | |
if(is_writable($localFile)) { | |
if($fp = fopen($localFile, 'w')){ | |
fwrite($fp, $response); | |
fclose($fp); | |
} | |
} | |
} | |
?> |
After you’re done, you can trigger a testrun to see if the script is properly executed. SSH into your server and type the following command: php -f /path/to/your/script.php
After running the script, the javascript file should appear within the directory you specified within the $uploadDir
variable. If that’s not the case, make sure PHP has the right permissions and can write files to the directory you specified.
Scheduling a Cronjob
Businesses advise you to load their javascript from their own resources for a very important reason. They want to control its contents:
- As a security measure,
- It allows them to roll out updates at once, whenever this is required.
The downside to this is that it will lower your score on Google Pagespeed, Pingdom, etc., because making external DNS requests is bad for your site’s performance and raises your loading time.
To achieve the best of both worlds, we combine the script you created earlier with a Cronjob, so it is triggered regularly and kept up-to-date.
The command you used earlier to test your script, will be extended into a syntax that Crontab understands. For this we need the following prerequisites:
- An interval for the script to be triggered (e.g. daily),
- The full path to the PHP executable, which can be found by entering
which php
into a terminal.
This would result in a command looking like this:
10 0 * * * /usr/bin/php -f /path/to/your/script.php >/dev/null 2>&1
This command triggers the script on a daily basis at 10 minutes past Midnight. You can use a crontab generator to generate your own cronjob.
After generating your cronjob, copy it and enter crontab -e
into a terminal. Crontab will open in editor mode (sometimes you first need to choose your favourite text editor. I suggest nano
) and all you have to do is paste the command on a new line. Save it, and you’re done!
All you need to do now is change the URLs within the provided javascript snippets. E.g. for Google Analytics, this would mean I’d change this:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','https://www.google-analytics.com/analytics.js','__gaTracker'); | |
__gaTracker('create', 'UA-XXXXXXXX-X', 'auto'); | |
__gaTracker('set', 'forceSSL', true); | |
__gaTracker('send','pageview'); |
To this:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','https://www.yoursite.com/analytics.js','__gaTracker'); | |
__gaTracker('create', 'UA-XXXXXXXX-X', 'auto'); | |
__gaTracker('set', 'forceSSL', true); | |
__gaTracker('send','pageview'); |
After this, make sure your changes are not influencing the results the script should generate. This differs per script, but e.g. for Google Analytics you could open up a private browser window, and visit your site, while you’re monitoring the Real Time traffic within your Analytics Dashboard.
If all is well, tap yourself on the shoulder! Perhaps you could even demand a standing ovation from your pets! After that, run your site through Pingdom and Pagespeed Insights and you’ll see your score is higher than before!

You’re done! You can repeat this tutorial for as many files as you’d like. Just make sure to test your changes afterwards! Because the fact that your tools work as they should, is more important than a high score in pingdom or pagespeed insights.
Nonetheless, hosting as many files as possible locally and minimizing DNS requests is better for your sites’ performance. So, do it when possible!
On my server with PHP7.3, I had to add four of these ‘ in line 7 and 10 in order to make this work. Otherwise I would have gotten a syntax error.
Hi Bernhard,
After your comment I noticed that some sort of auto formatting must’ve occurred. Lot’s of syntax error! Terribly sorry!
(Edit: they’re fixed now!)
Daan and others, has anyone tried to host google maps script locall with scheduled cron job?
I have a site I set this up on based on your tutorial however when I run it from ssh or cron or by hitting the link to the file in a browser it returns a 301 error message from cloudflare.. it does create and update the local file but its contents are
301 Moved Permanently
301 Moved Permanently
CloudFront
any thoughts? I am using litespeed enterprise server and their caching plugin with the site going through cloudflare..
Dear Daan, I’m noting you that HELL is still available, both as archive.org Download button and as a wp svn trunk… That being said, let me continue in a much friendlier manner so as not to make myself look like a lame tech support fool trolling a devops senior. You are rocking with your blog… After I read every single post, I will leave my 2 cents in a comment. Almost always as a snippet, link or such. Keep on doing the good work, man!