diff --git a/INSTALL.txt b/INSTALL.txt
index 7ee87be..983f5a4 100644
--- a/INSTALL.txt
+++ b/INSTALL.txt
@@ -9,7 +9,7 @@ REQUIRED:
 
 The origin website.
 
-$conf['stage_file_proxy_origin'] = 'http://example.com'; // no trailing slash
+$config['stage_file_proxy.settings']['origin'] = 'http://example.com'; // no trailing slash
 
 If the site is using HTTP Basic Authentication (the browser popup for username
 and password) you can embed those in the url. Be sure to URL encode any
@@ -18,11 +18,11 @@ special characters:
 For example, setting a user name of "myusername" and password as, "letme&in" the
 configuration would be the following:
 
-$conf['stage_file_proxy_origin'] = 'http://myusername:letme%26in@example.com';
+$config['stage_file_proxy.settings']['origin'] = 'http://myusername:letme%26in@example.com';
 
 OPTIONAL
 ========
-$conf['stage_file_proxy_use_imagecache_root'] = TRUE;
+$config['stage_file_proxy.settings']['use_imagecache_root'] = TRUE;
 
 Default is TRUE.
 
@@ -32,7 +32,7 @@ processed file, then send a header to the browser to refresh the image and let
 imagecache handle it. This will speed up future imagecache requests for the
 same original file.
 
-$conf['stage_file_proxy_hotlink'] = FALSE;
+$config['stage_file_proxy.settings']['hotlink'] = FALSE;
 
 Default is FALSE.
 
@@ -40,7 +40,7 @@ If this is true then Stage File Proxy will not transfer the remote file to the
 local machine, it will just serve a 301 to the remote file and let the origin
 webserver handle it.
 
-$conf['stage_file_proxy_origin_dir'] = 'sites/default/files';
+$config['stage_file_proxy.settings']['origin_dir'] = 'sites/default/files';
 
 Default is 'sites/default/files';
 
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..db12f05
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,8 @@
+{
+  "name": "drupal/stage_file_proxy",
+  "description": "Provides stage_file_proxy module.",
+  "type": "drupal-module",
+  "license": "GPL-2.0+",
+  "minimum-stability": "dev",
+  "require": { }
+}
diff --git a/config/install/stage_file_proxy.settings.yml b/config/install/stage_file_proxy.settings.yml
new file mode 100644
index 0000000..feb9b89
--- /dev/null
+++ b/config/install/stage_file_proxy.settings.yml
@@ -0,0 +1,4 @@
+hotlink: false
+origin: ''
+origin_dir: false
+use_imagecache_root: true
diff --git a/config/stage_file_proxy.schema.yml b/config/stage_file_proxy.schema.yml
new file mode 100644
index 0000000..4fc254d
--- /dev/null
+++ b/config/stage_file_proxy.schema.yml
@@ -0,0 +1,17 @@
+stage_file_proxy.settings:
+  type: mapping
+  label: 'Stage file proxy settings'
+  mapping:
+    hotlink:
+      type: booleam
+      label: "Hotlink"
+    origin:
+      type: string
+      label: "Origin"
+    origin_dir:
+      type: booelan
+      label: "Origin dir"
+    use_imagecache_root:
+      type: booelan
+      label: "Use imagecache root"
+"
diff --git a/config/stage_file_proxy.settings.yml b/config/stage_file_proxy.settings.yml
deleted file mode 100644
index 50d0ead..0000000
--- a/config/stage_file_proxy.settings.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-hotlink: false
-origin: false
-origin_dir: false
-use_imagecache_root: true
\ No newline at end of file
diff --git a/lib/Drupal/stage_file_proxy/EventSubscriber/ProxySubscriber.php b/src/EventSubscriber/ProxySubscriber.php
similarity index 63%
rename from lib/Drupal/stage_file_proxy/EventSubscriber/ProxySubscriber.php
rename to src/EventSubscriber/ProxySubscriber.php
index a0687c9..2ef54d6 100644
--- a/lib/Drupal/stage_file_proxy/EventSubscriber/ProxySubscriber.php
+++ b/src/EventSubscriber/ProxySubscriber.php
@@ -7,15 +7,15 @@
 
 namespace Drupal\stage_file_proxy\EventSubscriber;
 
-use Symfony\Component\HttpFoundation\Response;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Url;
+use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-
 use Drupal\stage_file_proxy\FetchManagerInterface;
-use Drupal\Component\Utility\String;
-use \Drupal\Component\Utility\Url;
 
 /**
  * Stage file proxy subscriber for controller requests.
@@ -25,29 +25,43 @@ class ProxySubscriber implements EventSubscriberInterface {
   /**
    * The manager used to fetch the file against.
    *
-   * @var \Drupal\stage_file_proxy\FetchManager
+   * @var \Drupal\stage_file_proxy\FetchManagerInterface
    */
   protected $manager;
 
   /**
+   * The logger.
+   *
+   * @var LoggerInterface
+   */
+  protected $logger;
+
+  /**
    * Construct the FetchManager.
    *
-   * @param \Drupal\stage_file_proxy\FetchManager $manager
+   * @param \Drupal\stage_file_proxy\FetchManagerInterface $manager
    *   The manager used to fetch the file against.
+   *
+   * @param \Psr\Log\LoggerInterface $logger
    */
-  public function __construct(FetchManagerInterface $manager) {
+  public function __construct(FetchManagerInterface $manager, LoggerInterface $logger) {
     $this->manager = $manager;
+    $this->logger = $logger;
   }
 
   /**
    * Fetch the file according the its origin.
    *
-   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   The Event to process.
    */
   public function checkFileOrigin(GetResponseEvent $event) {
     $file_dir = $this->manager->filePublicPath();
-    if (strpos(request_path(), $file_dir) !== 0) {
+    $uri = $event->getRequest()->getPathInfo();
+
+    $uri = Unicode::substr($uri, 1);
+
+    if (strpos($uri, '' . $file_dir) !== 0) {
       return;
     }
 
@@ -59,7 +73,7 @@ class ProxySubscriber implements EventSubscriberInterface {
       $remote_file_dir = $file_dir;
     }
 
-    $relative_path = drupal_substr(request_path(), drupal_strlen($file_dir) + 1);
+    $relative_path = Unicode::substr($uri, Unicode::strlen($file_dir) + 1);
 
     // Get the origin server.
     $server = \Drupal::config('stage_file_proxy.settings')->get('origin');
@@ -75,27 +89,30 @@ class ProxySubscriber implements EventSubscriberInterface {
       }
 
       $query = \Drupal::request()->query->all();
-      $query_parameters = Url::filterQueryParameters($query);
+      $query_parameters = UrlHelper::filterQueryParameters($query);
 
       if (\Drupal::config('stage_file_proxy.settings')->get('hotlink')) {
-        $location = url("$server/$remote_file_dir/$relative_path", array(
+
+        $location = Url::fromUri("$server/$remote_file_dir/$relative_path", array(
           'query' => $query_parameters,
           'absolute' => TRUE,
         ));
-        header("Location: $location");
-        exit;
+
       }
       elseif ($this->manager->fetch($server, $remote_file_dir, $relative_path)) {
         // Refresh this request & let the web server work out mime type, etc.
-        $location = url(request_path(), array('query' => $query_parameters));
-        header("Location: $location");
-        exit;
+        $location = Url::fromUri('base://' . $uri, array(
+          'query' => $query_parameters,
+          'absolute' => TRUE,
+        ))->toString();
       }
       else {
-        watchdog('stage_file_proxy', 'Stage File Proxy encountered an unknown error by retrieving file @file', array('@file' => $server . '/' . drupal_encode_path($remote_file_dir . '/' . $relative_path)), WATCHDOG_ERROR);
+        $this->logger->error('Stage File Proxy encountered an unknown error by retrieving file @file', array('@file' => $server . '/' . UrlHelper::encodePath($remote_file_dir . '/' . $relative_path)));
         throw new NotFoundHttpException();
-        exit;
       }
+
+      header("Location: $location");
+      exit;
     }
   }
 
@@ -106,7 +123,8 @@ class ProxySubscriber implements EventSubscriberInterface {
    *   An array of event listener definitions.
    */
   static function getSubscribedEvents() {
-    $events[KernelEvents::REQUEST][] = array('checkFileOrigin');
+    // Priority 240 is after ban middleware but before page cache.
+    $events[KernelEvents::REQUEST][] = array('checkFileOrigin', 240);
     return $events;
   }
 
diff --git a/lib/Drupal/stage_file_proxy/FetchManager.php b/src/FetchManager.php
similarity index 84%
rename from lib/Drupal/stage_file_proxy/FetchManager.php
rename to src/FetchManager.php
index bad0f4f..1583761 100644
--- a/lib/Drupal/stage_file_proxy/FetchManager.php
+++ b/src/FetchManager.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\stage_file_proxy;
 
-use Drupal\Component\Utility\Url;
-use Guzzle\Http\ClientInterface;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\StreamWrapper\PublicStream;
+use GuzzleHttp\ClientInterface;
 
 class FetchManager implements FetchManagerInterface {
 
@@ -21,10 +22,8 @@ class FetchManager implements FetchManagerInterface {
    */
   public function fetch($server, $remote_file_dir, $relative_path) {
     // Fetch remote file.
-    $url = $server . '/' . Url::encodePath($remote_file_dir . '/' . $relative_path);
-    $response = $this->client
-      ->get($url)
-      ->send();
+    $url = $server . '/' . UrlHelper::encodePath($remote_file_dir . '/' . $relative_path);
+    $response = $this->client->get($url);
 
     if ($response->getStatusCode() == 200) {
       // Prepare local target directory and save downloaded file.
@@ -42,7 +41,7 @@ class FetchManager implements FetchManagerInterface {
    * {@inheritdoc}
    */
   public function filePublicPath() {
-    return settings()->get('file_public_path', conf_path() . '/files');
+    return PublicStream::basePath();
   }
 
   /**
diff --git a/lib/Drupal/stage_file_proxy/FetchManagerInterface.php b/src/FetchManagerInterface.php
similarity index 97%
rename from lib/Drupal/stage_file_proxy/FetchManagerInterface.php
rename to src/FetchManagerInterface.php
index aeb9e2e..74015ab 100644
--- a/lib/Drupal/stage_file_proxy/FetchManagerInterface.php
+++ b/src/FetchManagerInterface.php
@@ -7,7 +7,8 @@
 
 namespace Drupal\stage_file_proxy;
 
-use Guzzle\Http\ClientInterface;
+
+use GuzzleHttp\ClientInterface;
 
 interface FetchManagerInterface {
 
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
new file mode 100644
index 0000000..b16f1fc
--- /dev/null
+++ b/src/Form/SettingsForm.php
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\stage_file_proxy\Form\SettingsForm.
+ */
+
+namespace Drupal\stage_file_proxy\Form;
+
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Component\Utility\Unicode;
+
+class SettingsForm extends ConfigFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'stage_file_proxy_settings';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return [
+      'stage_file_proxy.settings',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $field_type = NULL) {
+    $config = $this->config('stage_file_proxy.settings');
+
+    $form['origin'] = array(
+      '#type' => 'textfield',
+      '#title' => t('The origin website.'),
+      '#default_value' => $config->get('origin'),
+      '#description' => t("The origin website. For example: 'http://example.com' with no trailing slash. If the site is using HTTP Basic Authentication (the browser popup for username and password) you can embed those in the url. Be sure to URL encode any special characters:<br/><br/>For example, setting a user name of 'myusername' and password as, 'letme&in' the configuration would be the following: <br/><br/>'http://myusername:letme%26in@example.com';"),
+      '#required' => FALSE,
+    );
+
+    $stage_file_proxy_origin_dir = $config->get('origin_dir');
+    if (!$stage_file_proxy_origin_dir) {
+      $stage_file_proxy_origin_dir = $config->get('file_public_path');
+      if (empty($stage_file_proxy_origin_dir)) {
+        $stage_file_proxy_origin_dir = \Drupal::service('site.path') . '/files';
+      }
+    }
+    $form['origin_dir'] = array(
+      '#type' => 'textfield',
+      '#title' => t('The origin directory.'),
+      '#default_value' => $stage_file_proxy_origin_dir,
+      '#description' => t('If this is set then Stage File Proxy will use a different path for the remote files. This is useful for multisite installations where the sites directory contains different names for each url. If this is not set, it defaults to the same path as the local site.'),
+      '#required' => FALSE,
+    );
+
+    $form['use_imagecache_root'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Imagecache Root.'),
+      '#default_value' => $config->get('use_imagecache_root'),
+      '#description' => t("If this is true (default) then Stage File Proxy will look for /imagecache/ in the URL and determine the original file and request that rather than the processed file, then send a header to the browser to refresh the image and let imagecache handle it. This will speed up future imagecache requests for the same original file."),
+      '#required' => FALSE,
+    );
+
+    $form['hotlink'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Hotlink.'),
+      '#default_value' => $config->get('hotlink'),
+      '#description' => t("If this is true then Stage File Proxy will not transfer the remote file to the local machine, it will just serve a 301 to the remote file and let the origin webserver handle it."),
+      '#required' => FALSE,
+    );
+
+    $form['sslversion'] = array(
+      '#type' => 'textfield',
+      '#title' => t('SSL Version.'),
+      '#default_value' => $config->get('sslversion'),
+      '#description' => t('CURL will try to figure out which ssl version to use, but if it fails to do thatp roperly it can lead to getting an empty file and a 0 status code. The default is 3 which seems relatively common, but if you get 0 byte files you can try changing it to 2.'),
+      '#size' => 2,
+      '#maxlength' => 2,
+      '#required' => FALSE,
+    );
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function validateForm(array &$form, FormStateInterface $form_state) {
+
+    $origin = $form_state->getValue('origin');
+    $sslversion = $form_state->getValue('sslversion');
+
+    if (!empty($origin) && filter_var($origin, FILTER_VALIDATE_URL) === FALSE) {
+      $form_state->setErrorByName('origin', 'Origin needs to be a valid URL.');
+    }
+
+    if (!empty($origin) && Unicode::substr($origin, -1) === '/') {
+      $form_state->setErrorByName('origin', 'Origin URL cannot end in slash.');
+    }
+
+    if (!is_numeric($sslversion)) {
+      $form_state->setErrorByName('sslversion', 'You must enter a number for the SSL version.');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $config = $this->config('stage_file_proxy.settings');
+
+    $keys = array(
+      'origin',
+      'origin_dir',
+      'use_imagecache_root',
+      'hotlink',
+      'sslversion',
+    );
+    foreach ($keys as $key) {
+      $config->set($key, $form_state->getValue($key));
+    }
+    $config->save();
+  }
+
+}
diff --git a/stage_file_proxy.info.yml b/stage_file_proxy.info.yml
index 9f29aa1..a7def81 100644
--- a/stage_file_proxy.info.yml
+++ b/stage_file_proxy.info.yml
@@ -4,3 +4,4 @@ description: 'Proxies files from production server so you do not have to transfe
 package: Development
 version: VERSION
 core: 8.x
+configure: stage_file_proxy.admin_form
diff --git a/stage_file_proxy.links.menu.yml b/stage_file_proxy.links.menu.yml
new file mode 100644
index 0000000..1b53e82
--- /dev/null
+++ b/stage_file_proxy.links.menu.yml
@@ -0,0 +1,6 @@
+stage_file_proxy.admin_form:
+  title: 'Stage File Proxy settings'
+  parent: system.admin_config_system
+  description: 'Administrative interface for the Stage File Proxy module'
+  weight: -10
+  route_name: stage_file_proxy.admin_form
diff --git a/stage_file_proxy.module b/stage_file_proxy.module
deleted file mode 100644
index a7e5e5e..0000000
--- a/stage_file_proxy.module
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-/**
- * @file
- * Stage File Proxy Module.
- */
diff --git a/stage_file_proxy.permissions.yml b/stage_file_proxy.permissions.yml
new file mode 100644
index 0000000..e511695
--- /dev/null
+++ b/stage_file_proxy.permissions.yml
@@ -0,0 +1,4 @@
+administer stage_file_proxy settings:
+  title: 'Administer Stage File Proxy module'
+  description: 'Perform administration tasks for the Stage File Proxy module.'
+  restrict access: TRUE
diff --git a/stage_file_proxy.routing.yml b/stage_file_proxy.routing.yml
new file mode 100644
index 0000000..34c11fc
--- /dev/null
+++ b/stage_file_proxy.routing.yml
@@ -0,0 +1,7 @@
+stage_file_proxy.admin_form:
+  path: '/admin/config/system/stage_file_proxy'
+  defaults:
+    _form: '\Drupal\stage_file_proxy\Form\SettingsForm'
+    _title: Stage File Proxy Settings
+  requirements:
+    _permission: 'administer stage_file_proxy settings'
diff --git a/stage_file_proxy.services.yml b/stage_file_proxy.services.yml
index 249addc..9a4c363 100644
--- a/stage_file_proxy.services.yml
+++ b/stage_file_proxy.services.yml
@@ -1,10 +1,14 @@
 services:
   stage_file_proxy.fetch_manager:
     class: Drupal\stage_file_proxy\FetchManager
-    arguments: ['@http_default_client']
+    arguments: ['@http_client']
 
   stage_file_proxy.subscriber:
     class: Drupal\stage_file_proxy\EventSubscriber\ProxySubscriber
     tags:
       - { name: event_subscriber }
-    arguments: ['@stage_file_proxy.fetch_manager']
+    arguments: ['@stage_file_proxy.fetch_manager', '@logger.channel.stage_file_proxy']
+
+  logger.channel.stage_file_proxy:
+    parent: logger.channel_base
+    arguments: ['stage_file_proxy']
