Deploy a static website with AWS S3 and Paws

Amazon Web Services (AWS) is Amazon’s cloud services platform and S3 is the AWS file storage service. S3 is commonly used to host static websites. With Perl we have many modules for using AWS, but I like Paws, developed by Jose Luis Martinez which supports many AWS services, including S3. In this article I’ll walk you through a Perl script I developed to upload and maintain a static website using S3 and Paws.

AWS setup

To use AWS from the command line you’ll need a to generate a key id and secret key for your account which you can get from the AWS website. Once you login with your Amazon credentials, click on your account name and go to “My Security Credentials”. Once you have a key id and secret key, you need to create the credentials files as used by awscli. You can either install awscli and run aws configure, else create:

Change the region value to the AWS region you want to use, and replace the “XXX” values with your own key id and secret key values. These files are stored in a different location on Windows.

Create an S3 bucket

S3 organizes files by bucket. Every bucket has URI-like name, which is unique across AWS. So if you’re going to host a website on S3, you’ll need to create a bucket for the website. This can be done via the AWS web interface, the command-line app or with Paws:

The ACL argument specifies that the bucket can be read publicly, but not edited, which makes sense for website files. At some point, you’ll need to enable the “static web hosting” option for the bucket, but that’s not necessary to upload files to it.

Upload files to S3

S3 files are stored as objects in buckets. Every file has a key, which is similar to the filename. I’ve developed a script called s3-upload which uses Paws to upload files to S3 buckets. It uses Getopt::Long to parse command line options. It requires --bucket for the S3 bucket name, --region for the AWS region, and --files for the directory filepath:

I’ve omitted the subroutine definitions for brevity (see the source for details). The script begins by validating the input options, then creates an $s3 object. It calls get_remote_objects which returns a hashref of keys (files) and their last modified time currently in the bucket. It passes this to upload which only uploads files that have been modified since being uploaded to S3 (you don’t want to upload the entire website if only one file has changed). upload does many things, but essentially, it uses PutObject to upload files:

Here Key is the filename and Body the raw bytes of the file. The upload subroutine also returns a hashref of local keys and their last modified time. Optionally, the script can call delete_stale_objects which deletes files from S3 which do not exist in the local tree.

The script will print any files uploaded to STDOUT and all other output to STDERR. The intention is to make it possible to pipe the filenames uploaded to other programs. A useful one might be a Cloudfront script which invalidates the cache for any files uploaded.

More features

Whilst the above script does the job, there are some features missing that are useful for static websites. Firstly, you might want to specify the MIME type of the files being uploaded. This is so when browsers fetch the files, S3 responds with the correct content type header. Otherwise, HTML files may not be displayed as websites, images may be downloaded instead of displayed, and so on. I use Media::Type::Simple for this:

I’ve uploaded a copy of mime.types to the repo, and added a --mime-types option for the filepath to a mime.types file (defaulting to /etc/mime.types). Also not all media types are defined, so the code adds a custom definition for woff2. The mime type is passed to PutObject when a file is uploaded.

Other useful options supported by the script:

--strip- it seems cleaner to visit: /home than /home.html. The --strip option can be used to specify any extensions to strip from filenames

--max-age - set a cache control header to have browsers cache files instead of downloading them on every page

--force - override the default behavior and upload all files, regardless of whether they already exist in the S3 bucket

Site Map

Contact Us

License

Legal

Perl.com and the authors make no representations with respect to the accuracy or completeness of the contents of all work on this website and specifically disclaim all warranties, including without limitation warranties of fitness for a particular purpose. The information published on this website may not be suitable for every situation. All work on this website is provided with the understanding that Perl.com and the authors are not engaged in rendering professional services. Neither Perl.com nor the authors shall be liable for damages arising herefrom.