How to Move WordPress Media Files to Amazon S3 and CloudFront

Introduction

Amazon S3 provides scalable and reliable storage for static website content such as images, videos, and other media files. By moving WordPress media files to Amazon S3 and serving them through Amazon CloudFront, you can reduce the load on the WordPress server and improve media delivery performance for visitors.

Prerequisites

Before proceeding, make sure you have:

  1. A working WordPress website with administrator access.
  2. Access to the AWS Management Console.
  3. An AWS IAM user with the required permissions to access S3.
  4. An S3 bucket created for storing the WordPress media files.
  5. AWS Access Key ID and Secret Access Key.
  6. AWS CLI installed and configured on the WordPress server.
  7. The Amazon Web Services, Amazon S3, and CloudFront WordPress plugins installed and configured.
  8. A complete backup of the WordPress files and database.
  9. Sufficient AWS S3 storage and CloudFront configuration for the website.
  10. Caching should be temporarily disabled while performing the migration to avoid stale media URLs.

Implementation

In this post, we will explain moving wordpress media to Amazon CloudFront and S3.

Step 1: Install the following plugins on WordPress.

https://wordpress.org/plugins/amazon-web-services/

https://wordpress.org/plugins/amazon-s3-and-cloudfront/

Step 2: Configure your Amazon Web Services account here and create a new user in the AWS console.

Step 3: To create the AWS S3 bucket for your domain, create the new user in AWS and copy the AWS Access Key ID and Secret Access Key.

Step 4: Log in to your server and configure the AWS user using the following command.

aws configure

Step 5:Go to your WordPress site upload folder and run the following command.

aws s3 sync . s3://bucket-name/folder

Step 6: In the root folder, you can create a new file named aws-cdn-script.php using the following code.

<?php

    ini_set('display_errors', 1);
    error_reporting(E_ALL);

    /* ======================================================
    This script is NOT FULLY TESTED (not tested on Windows Server either)
    USE AT YOUR OWN RISK - development environment Ubuntu Linux 14.04.3 LTS

    The purpose of this script is for small websites and blogs
    to have their existing media to work through Amazon S3
    
    There's a great plugin, WP Offload S3, that we'll be tapping
    into...it works great for new media, but this is a quick
    fix for EXISTING media - media added before you start
    using WP Offload S3.

    This script is not fully tested, but should be useful for
    most situations.  It does create a backup of your database,
    though it's not tested to work 100% in all hosting scenarios.

    Put this file in your WordPress root directory, where wp-config.php is.
    You must also already have these 2 plugins installed and configured:
    - https://wordpress.org/plugins/amazon-web-services/
    - https://wordpress.org/plugins/amazon-s3-and-cloudfront/

    TURN OFF CACHING if you have it on your site.  Re-enable it after
    everything is done.

    ======================================================
    STEPS

    1) Manually upload all of your existing /wp-content/uploads/ files to the
    Amazon S3 bucket (the bucket you configured WP Offload S3 to use for new images)

    2) Add and configure plugins (the 2 listed above)

    3) Run this script from the root directory of your WordPress site

    ======================================================
    Developer: TJ Nevis
    Website: http://blog.TJNevis.com, http://NevisTechnology.com
    More Info: http://blog.tjnevis.com/wordpress-and-amazon-s3-quick-fix-for-existing-media-to-be-served-on-amazon-s3/
    Last Updated: 04/19/2016
    ====================================================== */

    require_once 'wp-config.php';
    require_once 'wp-content/plugins/amazon-s3-and-cloudfront/wordpress-s3.php';
    global $table_prefix;

    echo '<h1>Move Existing Media to Amazon S3</h1>';
    echo '<h3>By TJ Nevis [<a href="http://NevisTechnology.com?ref=S3Script" target="_blank">NevisTechnology.com</a>] Detailed blog post <a href="http://blog.tjnevis.com/wp-offload-s3-quick-fix-for-existing-media/" target="_blank">here</a></h3>';

    /* ======================================================
    First step, we're going to back up the database and
    save it in the the root directory of your site (where this file is)    
    ====================================================== */
    $dbHost = $wpdb->dbhost;
    $dbName = $wpdb->dbname;
    $dbPassword = $wpdb->dbpassword;
    $dbSaveFileLocation = dirname(__FILE__) . '/backup-' . date('Y-m-d-h:i:s', time()) . '.sql';
    $dbUser = $wpdb->dbuser;

    exec("mysqldump --user=$dbUser --password=$dbPassword --host=$dbHost $dbName > $dbSaveFileLocation");

    /* ======================================================
    Let's get set up with by grabbing WP Offload S3 values
    ====================================================== */
    amazon_web_services_require_files(); // Thanks k0nG
    $aws = new Amazon_Web_Services( __FILE__ ); // Thanks k0nG
    $awsS3 = new Amazon_S3_And_CloudFront(__FILE__, $aws); // Thanks k0nG
    $bucket = ( !empty( $awsS3->get_setting('cloudfront') ) ) ? $awsS3->get_setting('cloudfront') : $awsS3->get_setting('bucket'); // If you're using a CDN and the setting is saved, use that for CloudFront URL replcaement, otherwise use the S3 bucket name
    $region = $awsS3->get_setting('region'); // Thanks k0nG
    $folderPrefix = $awsS3->get_setting('object-prefix');

    /* ======================================================
    Check if the user is requesting to remove the existing media updates
    ====================================================== */
    if( isset($_GET['remove']) && $_GET['remove'] ) { // ?removeS3Update=true
        if( $dbConnection = mysqli_connect($dbHost, $dbUser, $dbPassword, $dbName) ) {

            $removeAmazonS3Info = "DELETE FROM " . $table_prefix . "postmeta WHERE meta_key = 'amazonS3_info';";
            $reversePostContentHref = updatePostContent(
                                            'href',
                                            $table_prefix . 'posts',
                                            get_site_url( $wpdb->blogid ) . '/' . $folderPrefix,
                                            "https://$bucket.s3.amazonaws.com/$folderPrefix",
                                            true
                                        );
            $reversePostContentSrc = updatePostContent(
                                            'src',
                                            $table_prefix . 'posts',
                                            get_site_url( $wpdb->blogid ) . '/' . $folderPrefix,
                                            "https://$bucket.s3.amazonaws.com/$folderPrefix",
                                            true
                                        );

            echo 'RUNNING COMMAND: ' . $removeAmazonS3Info . ' - '; 
            if( $dbConnection->query($removeAmazonS3Info) ) {
                echo ' <strong>TRUE, ' . $dbConnection->affected_rows . ' rows affected</strong><br />';
            }

            echo 'RUNNING COMMAND: ' . $reversePostContentHref . ' - ';
            if( $dbConnection->query($reversePostContentHref) ) {
                echo ' <strong>TRUE, ' . $dbConnection->affected_rows . ' rows affected</strong><br />';
            }

            echo 'RUNNING COMMAND: ' . $reversePostContentSrc . ' - '; 
            if( $dbConnection->query($reversePostContentSrc) ) {
                echo ' <strong>TRUE, ' . $dbConnection->affected_rows . ' rows affected</strong><br />';
            }   

        }

        echo '<h3>DONE with removing records for WP Offload S3 - reverted back to serving from the local server</h3>';

        exit(); // Don't do the rest of the script
    }

    /* ======================================================
    Start fresh, delete data thatis from WP Offload S3    
    ====================================================== */
    $wpdb->delete($table_prefix . 'postmeta',
        array(
            'meta_key'  => 'amazonS3_info'
        )
    );

    /* ======================================================
    Grab the attachments from the database, we'll need
    the meta_value (image name), and the post ID it's related to
    ====================================================== */
    $picturesToUpdate = $wpdb->get_results("SELECT * FROM " . $table_prefix . "postmeta WHERE meta_key = '_wp_attached_file'");

    foreach($picturesToUpdate as $picture) {
        $pictureMetaData = serialize(array(
            'bucket'    => $bucket,
            'key'       => $folderPrefix . $picture->meta_value,
            'region'    => $region // Thanks k0nG
        ));

        /* ======================================================
        Now let's insert the record that WP Offload S3 looks for
        to change the image URL to your S3 URL
        ====================================================== */
        $wpdb->insert($table_prefix . 'postmeta', 
            array(
                'post_id'   => $picture->post_id,
                'meta_key'  => 'amazonS3_info',
                'meta_value'  => $pictureMetaData
            )
        );

    }

    if( $dbConnection = mysqli_connect($dbHost, $dbUser, $dbPassword, $dbName) ) {

        $hrefMySQLUpdate = updatePostContent(
                                'href',
                                $table_prefix . 'posts',
                                get_site_url( $wpdb->blogid ) . '/' . $folderPrefix,
                                "https://$bucket.s3.amazonaws.com/$folderPrefix"
                            );
        $srcMySQLUpdate = updatePostContent(
                                'src',
                                $table_prefix . 'posts',
                                get_site_url( $wpdb->blogid ) . '/' . $folderPrefix,
                                "https://$bucket.s3.amazonaws.com/$folderPrefix"
                            );

        echo 'RUNNING COMMAND: ' . $hrefMySQLUpdate . ' - '; 
        if( $dbConnection->query($hrefMySQLUpdate) ) {
            echo ' <strong>TRUE, ' . $dbConnection->affected_rows . ' rows affected</strong><br />';
        }

        echo 'RUNNING COMMAND: ' . $srcMySQLUpdate . ' - ';
        if( $dbConnection->query($srcMySQLUpdate) ) {
            echo ' <strong>TRUE, ' . $dbConnection->affected_rows . ' rows affected</strong><br />';
        }

    }

    echo '<h3>DONE with adding records for WP Offload S3 to recognize the image on S3</h3>';



    function updatePostContent($type, $table, $blog, $s3bucket, $reverse = FALSE) {
        // $reverse is to remove the post_content updates and put them back to serving locally
        $from   = ( !$reverse ) ? $blog : $s3bucket;
        $to     = ( !$reverse ) ? $s3bucket : $blog;

        return "UPDATE $table SET post_content = replace(post_content, '$type=\"$from', '$type=\"$to');";
    }

?>

Step 7: In the URL, you can access this page like http://sitename.com/aws-cdn-script.php?remove=true, and after execution, the file will be removed.

Conclusion

Migrating WordPress media files to Amazon S3 and serving them through CloudFront can reduce the resource usage of the WordPress server and improve the delivery of static content. Before running the migration script, always maintain a verified backup of the database and media files.

After confirming that all media is loading correctly from S3/CloudFront, the temporary migration script should be removed from the WordPress root directory for security reasons. The migration should also be tested carefully on a staging environment before applying it to a production website.

FAQs

1. Why should WordPress media files be moved to Amazon S3?

Moving media files to Amazon S3 reduces the storage and bandwidth load on the WordPress server. It also provides scalable and highly durable storage for images and other static files.

2. What is the role of Amazon CloudFront?

Amazon CloudFront is a Content Delivery Network (CDN) that delivers files from locations closer to website visitors, which can improve media loading performance.

3. Do I need both Amazon S3 and CloudFront?

No. WordPress media can be served directly from Amazon S3. CloudFront is optional but recommended when you want CDN-based delivery and better global performance.

How to download files from S3 bucket to local machine or server

A Guide to upload Images to S3

Talk to our experts:

Looking for the right technology solution for your business?. Our experts can help with web hosting, domain registration, DevOps and cloud, software development, web applications, mobile applications, and enterprise solutions. Get in touch with our team here.

admin

Our team has expertise across software and web development, WordPress, e-commerce, mobile applications, UI/UX, cloud and infrastructure, DevOps, CI/CD, API integration, security, testing, automation, and technical support. The team also works with AI-based software solutions, LLMs, AI workflows, AI agents, and intelligent application development to help businesses automate processes and build smarter digital solutions. We focus on developing, deploying, maintaining, and optimising secure, scalable, and reliable technology solutions while helping businesses adopt modern technologies and drive digital transformation.

2 thoughts on “How to Move WordPress Media Files to Amazon S3 and CloudFront”

  1. Awesome- I know this is form 3 years ago, and wnat to give it a try. The only thing I am hesitant of is that WP S3 offloader (Offload media lite) plugin is giving it an extra subdirectory when moving them:

    /wp-content/uploads/2014/08/xyz.jpg
    becomes
    /wp-content/uploads/2014/08/12220703/xyz.jpg

    It is adding the “12220703” above.

    Any observations about this?

    Thank you by the way!

    Jon Q

Leave a Reply

Scroll to Top