More from this category
https://www.wpbeginner.com/wordpress-performance-speed/
++++++++++++++
CDN STUFF
========================================
If you’ve been working around web development for a while, you’ll know that the web has a need for speed. The truth is simple: speed matters and faster is considered better.
One “easy win” for increasing performance is using a CDN. We’ve discussed this several times at CSS-Tricks. It’s easy in that you aren’t rewriting a codebase or hand-editing content, but it’s an area of performance many of us might overlook because, well, hosting, servers and cPanels, oh my!
Thankfully, the process for integrating a CDN does not have to be hard. Ideally, you get things configured and then things like Google PageSpeed shriek with glee in the form of high performance scores which, in turn, yield to better search engine rankings, higher revenue, a raise for you and…you get the point.
This post is going to walk through the process for setting up a CDN to work on a custom web application, followed by some tricks we can use to get the most out of our work. Like I said, it doesn’t have to be hard, so let’s dive in and make the web faster!
It’s worth noting that setting up a CDN can be even simpler than what we’re covering here for those working with a CMS like WordPress, where plugins can help with the lift. We’re going to assume that not everyone has that luxury (shout out to web applications!) and proceed in the more traditional way.
Hold up, you lost me at CDN
It doesn’t hurt for us to review the basic concepts of what a CDN is and what they’re used for, so let’s start with that.
A CDN works by fetching static assets from your site’s server and caching them on their servers. When a browser makes a request to visit your site, the static contents are served via the CDN rather than by your site. This is faster because the CDN servers are globally distributed and serve as proxies that determine which server is located physically closest to your visitor, thus delivering content on a speedier, more convenient network. Hence the full name: Content Delivery Network.
A Content Delivery Network is globally distributed for optimized delivery of static assets
So, how does the browser know to get the resources from the CDN instead of your server? The URL of your static resources are replaced to point to the CDN server instead of the URL of your own site.
For example, say we want CDN to be configured as a subdomain. For CSS-Tricks, that might be something like cdn.css-tricks.com
and that will be the relative URL for which we base all our assets.
Put more succinctly put, the URLs of assets on our site like this:
[code style=”php”]https://css-tricks.com/image.jpg
https://css-tricks.com/style.css
https://css-tricks.com/script.js[/code]
…would become:
[code style=”php”]http://cdn.css-tricks.com/image.jpg
http://cdn.css-tricks.com/style.css
http://cdn.css-tricks.com/script.js[/code]
The browser sends the requests to your CDN rather than your server, taking the load off your server and making your whole site faster as a result.
Wait, two servers for one site?
Yes, to a degree, but it doesn’t really mean that you’re managing two servers.
What we’re talking about instead is using the CDN as a virtual layer that stands between your server and a user’s browser. That virtual layer listens in on the request the browser is making to your server and gladly responds with cached assets on behalf of the server.
In some cases, you may upload your asset directly to the CDN and take the entire burden off your server. What we’ll be looking at in this post instead is a process where the CDN fetches assets from your server and pre-caches them so there’s no need to upload to different servers and manage multiple locations.
How to implement a CDN on a Custom Application
Two of the most widely used CDN services are Amazon AWS and MaxCDN, though plenty of other services are certainly available. We’ll be focusing most of our attention on MaxCDN as an example of how to set things up.
Step 1: Choose a CDN and Register Your Site
Once you’ve decided that a CDN is the way to go for your web application, you’ll want to register for an account. There are many, many options out there and, rather than weigh the pros and cons of each (which might make a good future post), here are a few to get you started:
A common step when registering your account is to set up a pull zone or distribution. A pull zone or distribution is used by the CDN as a bucket for your everyday support files. It will automatically pull the data from a location that you specify upon the first request for the file. The files are served to the end user from either a subdomain of your CDN of choice or a custom domain (sub domain) of your choice that points to the CDN. The data in the bucket is automatically purged from the server after a customizable amount of time.
Step 2: Create your CDN URL
Your CDN URL is the URL all of your assets will point to once things have been set up. A good rule of thumb is to use a URL name that is easy to do a search and replace in your database for all of your existing URLs.
Like any other subdomain, this will need to be set up as a CNAME record in your host’s DNS settings.
Step 3: Point Your Assets to the CDN
Let’s look at a method for creating a variable for the CDN URL and how it can be used to help programmatically prepend the URL to our static resources. The reason we want to do this is that (1) it makes it tougher to make mistakes in our markup and (2) it is easier to maintain the URL should we need to change it.
We’ll do this is by defining a global CDN variable, then prepend this variable to our static resources URL. We define this variable at both the PHP level and the JavaScript level so that we have more flexibility in how we use it down the road. It also makes it easier for us if we decide to ditch the CDN because all we have to do is replace the variable with a simple /
to get things back to the relative path of our original server.
Note that the following examples are here to serve illustrate examples rather than to be used literally. Your actual usage may vary.
[
Step 3: Point Your Assets to the CDN
Let’s look at a method for creating a variable for the CDN URL and how it can be used to help programmatically prepend the URL to our static resources. The reason we want to do this is that (1) it makes it tougher to make mistakes in our markup and (2) it is easier to maintain the URL should we need to change it.
We’ll do this is by defining a global CDN variable, then prepend this variable to our static resources URL. We define this variable at both the PHP level and the JavaScript level so that we have more flexibility in how we use it down the road. It also makes it easier for us if we decide to ditch the CDN because all we have to do is replace the variable with a simple / to get things back to the relative path of our original server.
Note that the following examples are here to serve illustrate examples rather than to be used literally. Your actual usage may vary.
[code style=”php”]
<?php
define(‘cdnURL’, ‘http://cdn.css-tricks.com/’);
?>
<html>
<head>
<title>Hello World!</>
<script type=’text/javascript’>
/* Let’s define a javascript global for using the CDN inside scripts */
var cdnURL = ‘<?php echo cdnURL ?>’;
</script>
<link rel=’stylesheet’ href='<?php echo cdnURL ?>css/style.css’ />
</head>
<body>
<img src='<?php echo cdnURL ?>img/clickety-clack-site/june2021/logo.png’ />
<button>Submit</button>
<script type=’text/javascript’ src='<?php echo cdnURL ?>js/main.js’></script>
</body>
</html>
[/code]
Or, done in JavaScript:
[code style=”php”]
(function() {
var preloadImage = document.createElement(‘img’);
preloadImage.src = cdnURL + ‘img/clickety-clack-site/june2021/logo.png’;
})();
[/code]
This does require a slight change in your thought processes as a developer. Every static resource needs to get the cdnURL variable prepended to it.
Same thinking goes for your CSS. For example, we can also setup a global CDN variable and prepend it to our CSS resources using a CSS preprocessor, like LESS:
[code style=”php”]
@cdnURL: ‘http://cdn.css-tricks.com/’;
button {
background-image: url(‘@{cdnURL}img/clickety-clack-site/june2021/button.png’);
&:hover {
background-image: url(‘@{cdnURL}img/button_hover.png’);
}
}
…or Sass for that matter:
$cdnURL: ‘http://cdn.css-tricks.com/’;
button {
background-image: url(‘#{$cdnURL}img/clickety-clack-site/june2021/button.png’);
&:hover {
background-image: url(‘#{$cdnURL}img/button_hover.png’);
}
}
[/code]
The great thing about this is that you can switch off your CDN by simply setting the cdnURL
to /
which will recreate all of your relative URLs. This also has the advantage that should you want to switch the CDN URL, you just need to change the cdnURL
.
That is really the three-step process for setting up a CDN, linking it to your server and then pointing your existing assets to it for so the CDN can serve things up when requested.
Let’s talk about some advanced settings
Setting up the CDN wasn’t that scary, right? Now that we’ve gotten past the main hurdles, we can have some fun with advanced settings that optimize the way our assets are served.
TimeToLive (TTL)
CDNs typically have a TimeToLive (TTL) set for assets. This is a fancy way of telling the CDN how long (in seconds) before it should treat an asset as stale. At that point, it looks it up on the server again for a “fresh” copy.
The longer the TTL, the longer a “version” of that asset will stay with the CDN and continue to be served. The shorter the TTL, the more often it ditches the “version” of the asset it stores and goes back to the original server to look for an updated version.
MaxCDN cache settings screen for setting TTL expirations
Invalidating assets
The double-edged sword with TTL is that you can update an asset on your server and the change will not reflect on your site until the TTL has expired and the CDN makes its next stop to the server to find a new copy.
We can overcome this by invalidating an asset. The trick here is to change the filename on update. If the filename changes, the CDN doesn’t know any better and reads the newly named file as a completely new asset rather than an update to the existing one.
In other words, this: http://cdn.css-tricks.com/image100.jpg
…would be renamed to something like this: http://cdn.css-tricks.com/image10design-assets/ago-no-category/1.jpg
So long old version and hello new one!
Leveraging TTL for version control
Hey, so if the CDN is holding onto one version of an asset and there is a fresh copy of it living on our server that it hasn’t fetched yet, then we technically have two iterations of the same asset. We can use this to create a form of “version control” where updating our assets on the server doesn’t mean we automatically lose them and can revert to a past copy, if needed.
The really complicated way to do this is to rename all of your resources every time you make a change like we did when invalidating the asset. However, this is overkill from a maintenance perspective, even if we had to create a variable to do this like we did for the cdnURL
. We’re going to do some cheating instead because that’s how we roll on a blog that is built around tricks.
We’ll start by placing our static assets into their own folders so that this:
http://cdn.css-tricks.com/image.jpg
…becomes this: http://cdn.css-tricks.com/img100/image.jpg
To invalidate the file and force the CDN to serve the latest version, we would modify the subdirectory path like this: http://cdn.css-tricks.com/img101/image.jpg
See the difference? The filename stays the same but it now appears to live in a directory on the server. Again, the CDN does not know the difference and sees this as a completely new file. We just created a faux form of version control that happens directly in the folder!
But wait, we can do better.
Changing the number on a folder on every update is still a cumbersome process and yet another step in the maintenance of our site. What we can do instead is make a few small changes to the site’s .htaccess
file to do the heavy lifting for us.
We’ll outsmart the CDN by serving all our assets from the same folder, but make it look like it’s being served from a versioned folder, thanks to some rewrite rules.
[code style="php"]
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^ver-[0-9]+.[0-9]+.[0-9]+(.*)$ $1 [L,NC]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
</IfModule>
;[/code]
Ha! Now our server is spoofing the version number in the URL while delivering our example image.jpg
asset from the same path on the server where it was originally uploaded.
Invalidating all assets on demand
The last thing to round this out is to integrate our version control trick into our HTML markup so that the CDN will refresh all of the assets as soon as we want to fully invalidate them.
We’ll set up a config file where we define a variable for the version number, import that variable into our asset URL structure and then change the version number each time we want to push a refresh of our assets across the board.
Here’s what that might look like in a PHP config file:
[code style="php"]
VERSION
1.0.0
Configure::load('cdn');
define('VERSION', file_get_contents(APP.DS."Config".DS."VERSION"));
define('CDN', Configure::read('CDN.path').VERSION.'/'); /* the trailing slash is important */
Here’s how that might work as a LESS config file:
@VERSION: '1.0.0';
@import 'cdn';
@import 'version';
@CDNURL: '@{CDN}@{VERSION}/';
button {
background-image: url('@{CDNURL}img/clickety-clack-site/june2021/button.png');
&:hover {
background-image: url('@{CDNURL}img/button_hover.png');
}
}
[/code]
As you can see, you can also choose to use the CDN variable as a file, environment variable or whatever would work best for you. The principles are mostly the same, all will achieve the desired end result of incorporating the CDN URL (and version number) into all of our external resources.
CDN, FTW!
Hopefully this takes the scare out of setting up a CDN. It can certainly appear to be a daunting task and there absolutely are advanced settings that can get you lost in the weeds. The initial setup is what most of us need anyway and the benefits are profound when it comes to performance and user experience, among a host of other things (pun intended).
The truth is that it gets even simpler if you manage content on your site with a popular CMS like WordPress, where there are plugins galore to streamline the process even further.
Even if we do not have the luxury of plugins, setting up a CDN can still be pretty straightforward, even for the least hosting-oriented of us. The setup is the largest hurdle and we were able to break that down into three basic steps. All else flows nicely from there.
++++++++++++++++++++++++++++++++++
https://docs.wp-rocket.me/article/311-cdn-not-working-files-not-sent-to-cdn
CDN not working, files not sent to CDN
If, after activating the CDN option and entering your CNAME in WP Rocket, you see that your files are not being served from your CDN, please check the following.
In your WordPress dashboard, go to Settings → General and check how your site URL is written: Does it have the www in front of the domain?
Technically speaking, the www and non-www version of your domain name are 2 separate domains. When re-writing the URLs of your files to your CDN, WP Rocket looks for the domain version as specified in your Site Address settings. Any other version is considered external and will not be re-written with the CDN url.
So if you use www in your site settings, you should make sure all links to your files also contain it, otherwise they will not be re-written to your CDN.
In this example, there are two images. One image URI uses http://www.lucybeer.com and one uses http://lucybeer.com.
Only the the first image will get re-written to the CDN, the other will not because of the lack of www:
CDN for logged-in users
The replacement of your domain URL to the CDN URL will only apply on cached pages. On pages that are not cached (for whatever reason), regular WordPress URLs get applied for images, CSS, and JavaScript files.
Logged-in users will only see files from the CDN if the User Cache option is enabled.
Please note: If you're using a custom media uploads directory, the wp_uploads_dir function is used to determine the correct path.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
https://developer.wordpress.org/reference/functions/wp_upload_dir/
Browse: Home / Reference / Functions / wp_upload_dir()
wp_upload_dir( string $time = null, bool $create_dir = true, bool $refresh_cache = false )
Returns an array containing the current upload directory’s path and URL.
CONTENTS
Description
Related
Uses
Used By
Source
Changelog
User Contributed Notes
Description #Description
Checks the ‘upload_path’ option, which should be from the web root folder, and if it isn’t empty it will be used. If it is empty, then the path will be ‘WP_CONTENT_DIR/uploads’. If the ‘UPLOADS’ constant is defined, then it will override the ‘upload_path’ option and ‘WP_CONTENT_DIR/uploads’ path.
The upload URL path is set either by the ‘upload_url_path’ option or by using the ‘WP_CONTENT_URL’ constant and appending ‘/uploads’ to the path.
If the ‘uploads_use_yearmonth_folders’ is set to true (checkbox if checked in the administration settings panel), then the time will be used. The format will be year first and then month.
If the path couldn’t be created, then an error will be returned with the key ‘error’ containing the error message. The error suggests that the parent directory is not writable by the server.
$time
(string) (Optional) Time formatted in 'yyyy/mm'.
Default value: null
$create_dir
(bool) (Optional) Whether to check and create the uploads directory. Default true for backward compatibility.
Default value: true
$refresh_cache
(bool) (Optional) Whether to refresh the cache.
Default value: false
Return #Return
(array) Array of information about the upload directory.
'path'
(string) Base directory and subdirectory or full path to upload directory.
'url'
(string) Base URL and subdirectory or absolute URL to upload directory.
'subdir'
(string) Subdirectory if uploads use year/month folders option is on.
'basedir'
(string) Path without subdir.
'baseurl'
(string) URL path without subdir.
'error'
(string|false) False or error message.
Top ↑
More Information #More Information
Note that using this function will create a subfolder in your Uploads folder corresponding to the queried month (or current month, if no $time argument is provided), if that folder is not already there. You don’t have to upload anything in order for this folder to be created.
For creating custom folder for users
[code style="php"]
$current_user = wp_get_current_user();
$upload_dir = wp_upload_dir();
if ( isset( $current_user->user_login ) && ! empty( $upload_dir['basedir'] ) ) {
$user_dirname = $upload_dir['basedir'].'/'.$current_user->user_login;
if ( ! file_exists( $user_dirname ) ) {
wp_mkdir_p( $user_dirname );
}
}[/code]
Folder Name #Folder Name
In case you want to move the /uploads folder, you’ll have to use the UPLOADS constant. It normally shouldn’t get used, as it only get’s defined when ms_default_constants() is run (only multisite), but you can simply set:
define( 'UPLOADS', trailingslashit( WP_CONTENT_DIR ) . 'custom_uploads_name' );
in a single site install and it will just work, as the public directory structure function wp_upload_dir() sets it up, when it was defined:
$dir = ABSPATH . UPLOADS;
[code style="php"]
// returns `false` if the UPLOADS constant is not defined
$upload_dir_name = false;
if ( defined( 'UPLOADS' ) ) {
str_replace( trailingslashit( WP_CONTENT_DIR ), '', untrailingslashit( UPLOADS ) );
}
[/code]]
Related #Related
Uses Description
wp-includes/functions.php: _wp_upload_dir()
A non-filtered, non-cached version of wp_upload_dir() that doesn’t check the path.
wp-includes/l10n.php: __()
Retrieve the translation of $text.
wp-includes/formatting.php: wp_basename()
i18n friendly version of basename()
wp-includes/formatting.php: esc_html()
Escaping for HTML blocks.
wp-includes/load.php: get_current_blog_id()
Retrieve the current site ID.
[code style="php"]
wp-admin/includes/class-wp-debug-data.php: WP_Debug_Data::debug_data()
Static function for generating site debug data when required.
wp-includes/functions.php: wp_privacy_exports_dir()
Returns the directory used to store personal data export files.
wp-includes/functions.php: wp_privacy_exports_url()
Returns the URL of the directory used to store personal data export files.
wp-includes/functions.php: wp_get_upload_dir()
Retrieves uploads directory information.
wp-admin/includes/file.php: _wp_handle_upload()
Handle PHP uploads in WordPress, sanitizing file names, checking extensions for mime type, and moving the file to the appropriate directory within the uploads directory.
[/code]
File: wp-includes/functions.php
[code style="php"]
function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
static $cache = array(), $tested_paths = array();
$key = sprintf( '%d-%s', get_current_blog_id(), (string) $time );
if ( $refresh_cache || empty( $cache[ $key ] ) ) {
$cache[ $key ] = _wp_upload_dir( $time );
}
/**
* Filters the uploads directory data.
*
* @since 2.0.0
*
* @param array $uploads {
* Array of information about the upload directory.
*
* @type string $path Base directory and subdirectory or full path to upload directory.
* @type string $url Base URL and subdirectory or absolute URL to upload directory.
* @type string $subdir Subdirectory if uploads use year/month folders option is on.
* @type string $basedir Path without subdir.
* @type string $baseurl URL path without subdir.
* @type string|false $error False or error message.
* }
*/
$uploads = apply_filters( 'upload_dir', $cache[ $key ] );
if ( $create_dir ) {
$path = $uploads['path'];
if ( array_key_exists( $path, $tested_paths ) ) {
$uploads['error'] = $tested_paths[ $path ];
} else {
if ( ! wp_mkdir_p( $path ) ) {
if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
$error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
} else {
$error_path = wp_basename( $uploads['basedir'] ) . $uploads['subdir'];
}
$uploads['error'] = sprintf(
/* translators: %s: Directory path. */
__( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
esc_html( $error_path )
);
}
$tested_paths[ $path ] = $uploads['error'];
}
}
return $uploads;
}
Expand full source code View on Trac
[/code]
User Contributed Notes #User Contributed Notes
Skip to note 1 content
You must log in to vote on the helpfulness of this noteVote results for this note:17You must log in to vote on the helpfulness of this noteContributed by Codex — 5 years ago
More in-depth break down of the data returned.
[code style="php"]
<?php
$upload_dir = wp_upload_dir(); // Array of key => value pairs
/*
$upload_dir now contains something like the following (if successful)
Array (
[path] => C:pathtowordpresswp-contentuploads2010\05
[url] => http://example.com/wp-content/uploads/2010/05
[subdir] => /2010/05
[basedir] => C:pathtowordpresswp-contentuploads
[baseurl] => http://example.com/wp-content/uploads
[error] =>
)
// Descriptions
[path] - base directory and sub directory or full path to upload directory.
[url] - base url and sub directory or absolute URL to upload directory.
[subdir] - sub directory if uploads use year/month folders option is on.
[basedir] - path without subdir.
[baseurl] - URL path without subdir.
[error] - set to false.
*/
echo $upload_dir['path'] . '<br />';
echo $upload_dir['url'] . '<br />';
echo $upload_dir['subdir'] . '<br />';
echo $upload_dir['basedir'] . '<br />';
echo $upload_dir['baseurl'] . '<br />';
echo $upload_dir['error'] . '<br />';
$upload_url = ( $upload_dir['url'] );
$upload_url_alt = ( $upload_dir['baseurl'] . $upload_dir['subdir'] );
// Now echo the final result
echo $upload_url . '<br />'; // Output - http://example.com/wp-content/uploads/2010/05
// Using year and month based folders, the below will be the same as the line above.
echo $upload_url_alt . '<br />'; // Output - http://example.com/wp-content/uploads/2010/05
?>
[/code]]
Log in to add feedback
Skip to note 2 content
You must log in to vote on the helpfulness of this noteVote results for this note:10You must log in to vote on the helpfulness of this noteContributed by dino golman — 2 years ago
Strangely wp_upload_dir doesn’t return https for SSL websites. Hopefully they fix the issue soon. Here is the code below where you can get the upload dir and url in right way:
[code style="php"]
/**
* Get the upload URL/path in right way (works with SSL).
*
* @param $param string "basedir" or "baseurl"
* @return string
*/
function fn_get_upload_dir_var( $param, $subfolder = '' ) {
$upload_dir = wp_upload_dir();
$url = $upload_dir[ $param ];
if ( $param === 'baseurl' && is_ssl() ) {
$url = str_replace( 'http://', 'https://', $url );
}
return $url . $subfolder;
}
Expand full source code
[/code]]
[code style="php"]
// Get wp-content/uploads/myfolder by URL
fn_get_upload_dir_var( 'baseurl', 'myfolder' );
// Now get full path of the same folder
fn_get_upload_dir_var( 'basedir', 'myfolder' );
[/code]
Log in to add feedbackShow Feedback
Skip to note 3 content
You must log in to vote on the helpfulness of this noteVote results for this note:2You must log in to vote on the helpfulness of this noteContributed by abellowins — 4 years ago
I needed to NOT rely on Gravatar at all, and just use the buddypress profile avatar. Here is the result.
[code style="php"]
$avatar_url = bp_core_fetch_avatar(array('item_id'=>$user->ID,'html'=>false));
if (strpos($avatar_url, 'www.gravatar.com/avatar') !== false) {
$upload_dir = wp_upload_dir();
$avatar_url = $upload_dir['baseurl'] . '/avatars/pi-gravatar.jpg';
}
[/code]