Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Visit the Heroku Blog

    Find news and updates from Heroku in the blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Working with AI
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • PHP
  • Working with PHP
  • Uploading Files to S3 in PHP

Uploading Files to S3 in PHP

English — 日本語に切り替える

Last updated December 22, 2024

Table of Contents

  • S3 setup
  • Application setup
  • Using the AWS SDK for PHP
  • Setting application config vars
  • Handling file uploads

If your application needs to receive files uploaded by users, you need to make sure these uploads are stored in a central and durable location.

With Heroku’s ephemeral filesystem, any information written to a dyno’s filesystem will be lost when the dyno is restarted. Instead, Heroku recommends backing services. For file and media storage, Amazon’s Simple Storage Service (S3) is a great solution.

This article demonstrates how to set up a PHP application to use S3 for storing file uploads.

S3 setup

To follow this example, we’re assuming that you already have an Amazon Web Services (AWS) account set up, and you’re in possession of an AWS Access Key pair (access key ID and secret access key), see How do I Get Security Credentials? in the AWS documentation.

You’ll also need to create a new S3 bucket using your account, or re-use an existing one.

Read our Using AWS S3 to Store Static Assets and File Uploads guide for more information; it contains detailed background knowledge, step-by-step instructions for getting started with S3, and useful tips and tricks.

Application setup

If you haven’t created a Heroku application and Git repository yet, do so first:

$ mkdir heroku-s3-example
$ cd heroku-s3-example
$ git init
$ heroku create

Using the AWS SDK for PHP

The easiest way to install the AWS SDK for PHP is using Composer. The composer require command is the easiest way (alternatively, you can add the aws/aws-sdk-php package to composer.json by hand):

$ composer require aws/aws-sdk-php:~3.0

If you haven’t already done so, now is a good time to add the vendor/ directory to your .gitignore; you only want your composer.json and composer.lock, but not the vendor/ directory, under version control:

$ echo "vendor" >> .gitignore

Now you can commit everything:

$ git add composer.* .gitignore
$ git commit -m "use aws/aws-sdk-php"

Setting application config vars

Hard-coding database connection information, security credentials, or other runtime settings is not a good idea. Instead, you should store such information in an application’s environment and read it from there at runtime.

Any config var you set on your Heroku application will be available at runtime using getenv() or in $_ENV/$_SERVER. We will use this mechanism to dynamically read the AWS security keys (key ID and secret key) and the S3 bucket name in our code; all you need to do is set these three bits information (the code below uses “aaa”, “bbb” and “ccc” placeholders) as config vars using the Heroku CLI:

$ heroku config:set AWS_ACCESS_KEY_ID=aaa AWS_SECRET_ACCESS_KEY=bbb S3_BUCKET=ccc

All that’s missing now is some code to handle a file upload!

Handling file uploads

Next, we’ll build a very simple script that accepts a file to upload in the browser, and stores it on S3 under the same name it had on the client’s computer.

This is a very simple example without proper validation and using verbatim file names from the client. Make sure you always validate the file’s name and contents e.g. using fileinfo, and generate a custom filename or use another method of ensuring you’re not overwriting an existing file. You may also wish to put file type and size limits in place.

Put the following into a file named index.php:

<?php
require('vendor/autoload.php');
// this will simply read AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from env vars
$s3 = new Aws\S3\S3Client([
    'version'  => '2006-03-01',
    'region'   => 'us-east-1',
]);
$bucket = getenv('S3_BUCKET')?: die('No "S3_BUCKET" config var in found in env!');
?>
<html>
    <head><meta charset="UTF-8"></head>
    <body>
        <h1>S3 upload example</h1>
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['userfile']) && $_FILES['userfile']['error'] == UPLOAD_ERR_OK && is_uploaded_file($_FILES['userfile']['tmp_name'])) {
    // FIXME: you should add more of your own validation here, e.g. using ext/fileinfo
    try {
        // FIXME: you should not use 'name' for the upload, since that's the original filename from the user's computer - generate a random filename that you then store in your database, or similar
        $upload = $s3->upload($bucket, $_FILES['userfile']['name'], fopen($_FILES['userfile']['tmp_name'], 'rb'), 'public-read');
?>
        <p>Upload <a href="<?=htmlspecialchars($upload->get('ObjectURL'))?>">successful</a> :)</p>
<?php } catch(Exception $e) { ?>
        <p>Upload error :(</p>
<?php } } ?>
        <h2>Upload a file</h2>
        <form enctype="multipart/form-data" action="<?=$_SERVER['PHP_SELF']?>" method="POST">
            <input name="userfile" type="file"><input type="submit" value="Upload">
        </form>
    </body>
</html>

The example sets the ACL for the uploaded file so it’s publicly readable. If you want different permissions, adjust the example accordingly. You can also use a bucket policy to define what the default permissions for uploaded files in a bucket should be.

You can now add, commit and push this to Heroku:

$ git add index.php
$ git commit -m "file upload test form"
$ git push heroku master

After the deploy has finished, you can run heroku open or manually point your browser to your application to test it. Select a file from your computer (e.g. an image or text file) and upload it. If all went well, a success message will appear with a link to the uploaded file.

Keep reading

  • Working with PHP

Feedback

Log in to submit feedback.

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices