diff --git a/S3fsStreamWrapper.inc b/S3fsStreamWrapper.inc
index ccbb1f2..68fe661 100644
--- a/S3fsStreamWrapper.inc
+++ b/S3fsStreamWrapper.inc
@@ -366,6 +366,7 @@ class S3fsStreamWrapper implements DrupalStreamWrapperInterface {
       'timeout' => 60,
       'forced_saveas' => FALSE,
       'api_args' => array('Scheme' => !empty($this->config['use_https']) ? 'https' : 'http'),
+      'custom_GET_args' => array(),
     );
 
     // Presigned URLs.
@@ -425,7 +426,7 @@ class S3fsStreamWrapper implements DrupalStreamWrapperInterface {
     // ensure that browser caches will be bypassed upon version changes.
     $meta = $this->_read_cache($this->uri);
     if (!empty($meta['version'])) {
-      $external_url .= (strpos($external_url, '?') === FALSE ? '?' : '&') . $meta['version'];
+      $external_url = $this->_append_get_arg($external_url, $meta['version']);
     }
 
     // Torrents can only be created for publicly-accessible files:
@@ -435,12 +436,19 @@ class S3fsStreamWrapper implements DrupalStreamWrapperInterface {
       foreach ($this->torrents as $blob) {
         if (preg_match("^$blob^", $uri)) {
           // You get a torrent URL by adding a "torrent" GET arg.
-          $external_url .= (strpos($external_url, '?') === FALSE ? '?' : '&') . 'torrent';
+          $external_url = $this->_append_get_arg($external_url, 'torrent');
           break;
         }
       }
     }
 
+    // If another module added a 'custom_GET_args' array to the url settings, process it here.
+    if (!empty($url_settings['custom_GET_args'])) {
+      foreach ($url_settings['custom_GET_args'] as $name => $value) {
+        $external_url = $this->_append_get_arg($external_url, $name, $value);
+      }
+    }
+
     return $external_url;
   }
 
@@ -1570,6 +1578,25 @@ class S3fsStreamWrapper implements DrupalStreamWrapperInterface {
       debug($msg);
     }
   }
+
+  /**
+   * Helper function to safely append a GET argument to a given base URL.
+   *
+   * @param string $base_url
+   *   The URL onto which the GET arg will be appended.
+   * @param string $name
+   *   The name of the GET argument.
+   * @param string $value
+   *   The value of the GET argument. Optional.
+   */
+  protected static function _append_get_arg($base_url, $name, $value = NULL) {
+    $separator = strpos($base_url, '?') === FALSE ? '?' : '&';
+    $new_url = "{$base_url}{$separator}{$name}";
+    if ($value !== NULL) {
+      $new_url .= "=$value";
+    }
+    return $new_url;
+  }
 }
 
 // Guzzle\Http\CachingEntityBody is only defined once the SDK has been loaded,
diff --git a/s3fs.api.php b/s3fs.api.php
index 3ac3746..ca17287 100644
--- a/s3fs.api.php
+++ b/s3fs.api.php
@@ -26,6 +26,12 @@
  *     - 'timeout': (int) Time in seconds before a pre-signed URL times out.
  *     - 'api_args': array of additional arguments to the getObject() function:
  *       http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_getObject
+ *     - 'custom_GET_args': (array) Implementing this hook allows you to add
+ *       your own set of custom GET arguments to the S3 URLs of your files.
+ *       If your custom args' keys start with "x-", S3 will ignore them, but
+ *       still log them:
+ *       http://docs.aws.amazon.com/AmazonS3/latest/dev/LogFormat.html#LogFormatCustom
+ *
  * @param string $s3_file_path
  *   The path to the file within your S3 bucket. This includes the prefixes
  *   which might be added (e.g. s3fs-public/ for public:// files, or the
@@ -40,6 +46,11 @@ function hook_s3fs_url_settings_alter(&$url_settings, $s3_file_path) {
     $url_settings['presigned_url'] = TRUE;
     $url_settings['timeout'] = 10;
   }
+
+  // An example of adding a custom GET argument to all S3 URLs that
+  // records the name of the currently logged in user.
+  global $user;
+  $url_settings['custom_GET_args']['x-user'] = $user->name;
 }
 
 /**
