diff --git a/_test/tests/inc/common_ml.test.php b/_test/tests/inc/common_ml.test.php
index 6f3b71db446f3f86b56c6bcb2b0d6fc5fcddcce0..415c0a88de4e8f178714af144bb37426ada82279 100644
--- a/_test/tests/inc/common_ml.test.php
+++ b/_test/tests/inc/common_ml.test.php
@@ -90,6 +90,25 @@ class common_ml_test extends DokuWikiTest {
         $this->assertEquals($expect, ml($id, $args));
     }
 
+    function test_ml_img_external() {
+        global $conf;
+        $conf['useslash']   = 0;
+        $conf['userewrite'] = 0;
+
+        $ids  = array(
+            'https://example.com/lib/tpl/dokuwiki/images/logo.png',
+            'http://example.com/lib/tpl/dokuwiki/images/logo.png',
+            'ftp://example.com/lib/tpl/dokuwiki/images/logo.png'
+        );
+
+        foreach($ids as $id) {
+            $tok = media_get_token($id, 0, 0);
+
+            $expect = DOKU_BASE.$this->script.'?tok='.$tok.'&media='.rawurlencode($id);
+            $this->assertEquals($expect, ml($id));
+        }
+    }
+
     function test_ml_imgresize_array_external() {
         global $conf;
         $conf['useslash']   = 0;
@@ -107,8 +126,24 @@ class common_ml_test extends DokuWikiTest {
             $tok = media_get_token($id, $w, 0);
             $hash = substr(PassHash::hmac('md5', $id, auth_cookiesalt()), 0, 6);
 
-            $expect = DOKU_BASE.$this->script.'?hash='.$hash.'&w='.$w.'&tok='.$tok.'&media='.rawurlencode($id);
+            $expect = DOKU_BASE.$this->script.'?w='.$w.'&tok='.$tok.'&media='.rawurlencode($id);
             $this->assertEquals($expect, ml($id, $args));
         }
+
+        $h    = 50;
+        $args = array('h' => $h);
+        $tok = media_get_token($id, $h, 0);
+
+        $expect = DOKU_BASE.$this->script.'?h='.$h.'&tok='.$tok.'&media='.rawurlencode($id);
+        $this->assertEquals($expect, ml($id, $args));
+
+        $w    = 80;
+        $h    = 50;
+        $args = array('w' => $w, 'h' => $h);
+        $tok = media_get_token($id, $w, $h);
+
+        $expect = DOKU_BASE.$this->script.'?w='.$w.'&h='.$h.'&tok='.$tok.'&media='.rawurlencode($id);
+        $this->assertEquals($expect, ml($id, $args));
+
     }
 }
diff --git a/_test/tests/lib/exe/fetch_imagetoken.test.php b/_test/tests/lib/exe/fetch_imagetoken.test.php
index 9e5b6e4a26b21a0c8a7c6c9bf75e58efdd71268c..99e6425570e5ce08c3b48750afc7653c2c89190f 100644
--- a/_test/tests/lib/exe/fetch_imagetoken.test.php
+++ b/_test/tests/lib/exe/fetch_imagetoken.test.php
@@ -23,10 +23,10 @@ class fetch_imagetoken_test extends DokuWikiTest {
         parent::setUp();
 
         global $conf;
-        $conf['sendfile'] = 0;
+        $conf['xsendfile'] = 0;
 
         global $MIME, $EXT, $CACHE, $INPUT;    // variables fetch creates in global scope -- should this be in fetch?
-    }
+    }
 
     function getUri() {
        $w = $this->width ? 'w='.$this->width.'&' : '';
@@ -39,14 +39,14 @@ class fetch_imagetoken_test extends DokuWikiTest {
         $request = new TestRequest();
         return $request->get(array(),str_replace('{%token%}',$token,$this->getUri()));
     }
-
-    /**
+
+    /**
      *  modified image request with valid token
      *  expect: header with mime-type
      *  expect: content
      *  expect: no error response
-     */
-    function test_valid_token(){
+     */
+    function test_valid_token(){
         $valid_token = 'tok='.media_get_token($this->media, $this->width, $this->height).'&';
         $response = $this->fetchResponse($valid_token);
         $this->assertTrue((bool)$response->getHeader('Content-Type'));
@@ -54,24 +54,24 @@ class fetch_imagetoken_test extends DokuWikiTest {
         
         $status_code = $response->getStatusCode();
         $this->assertTrue(is_null($status_code) || (200 == $status_code));
-    }
-    
-    /**
+    }
+    
+    /**
      *  modified image request with invalid token
-     *  expect: 412 status code
-     */
-    function test_invalid_token(){
-        $invalid_token = 'tok='.media_get_token('junk',200,100).'&';
-        $this->assertEquals(412,$this->fetchResponse($invalid_token)->getStatusCode());
-    }
-    
-    /**
-     *  modified image request with no token
      *  expect: 412 status code
-     */
-    function test_missing_token(){
-        $no_token = '';
-        $this->assertEquals(412,$this->fetchResponse($notoken)->getStatusCode());
+     */
+    function test_invalid_token(){
+        $invalid_token = 'tok='.media_get_token('junk',200,100).'&';
+        $this->assertEquals(412,$this->fetchResponse($invalid_token)->getStatusCode());
+    }
+    
+    /**
+     *  modified image request with no token
+     *  expect: 412 status code
+     */
+    function test_missing_token(){
+        $no_token = '';
+        $this->assertEquals(412,$this->fetchResponse($no_token)->getStatusCode());
     }
 
     /**
diff --git a/_test/tests/lib/exe/fetch_statuscodes_external.test.php b/_test/tests/lib/exe/fetch_statuscodes_external.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..79a45ec93c6dbf28ebda345a1e377d839aa58822
--- /dev/null
+++ b/_test/tests/lib/exe/fetch_statuscodes_external.test.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @group internet
+ */
+class fetch_statuscodes_external_test extends DokuWikiTest {
+
+    private $media = 'http://www.google.com/images/srpr/logo3w.png'; //used in media_get_from_url test too
+    private $width = 200;
+    private $height = 0;
+
+    function setUp() {
+
+        header('X-Test: check headers working');
+        $header_check = function_exists('xdebug_get_headers') ? xdebug_get_headers() : headers_list();
+        if(empty($header_check)) {
+            $this->markTestSkipped('headers not returned, perhaps your sapi does not return headers, try xdebug');
+        } else {
+            header_remove('X-Test');
+        }
+
+        parent::setUp();
+
+        global $conf;
+        $conf['fetchsize'] = 500 * 1024; //500kb
+        $conf['xsendfile'] = 0;
+
+        global $MIME, $EXT, $CACHE, $INPUT; // variables fetch creates in global scope -- should this be in fetch?
+    }
+
+    function getUri() {
+        $w = $this->width ? 'w='.$this->width.'&' : '';
+        $h = $this->height ? 'h='.$this->height.'&' : '';
+        return '/lib/exe/fetch.php?'.$w.$h.'{%token%}media='.rawurlencode($this->media);
+    }
+
+    function fetchResponse($token) {
+        $request = new TestRequest();
+        return $request->get(array(), str_replace('{%token%}', $token, $this->getUri()));
+    }
+
+    /**
+     *  modified image request with valid token
+     *  and not-modified image request with valid token
+     *
+     *  expect: header with mime-type
+     *  expect: content
+     *  expect: no error response
+     */
+    function test_valid_token() {
+        $valid_token_resize = 'tok='.media_get_token($this->media, $this->width, $this->height).'&';
+
+        $this->handlevalidresponse($valid_token_resize);
+
+        //original size
+        $this->width          = $this->height = 0;
+        $valid_token_original = 'tok='.media_get_token($this->media, $this->width, $this->height).'&';
+
+        $this->handlevalidresponse($valid_token_original);
+
+    }
+
+    /**
+     * Performs asserts for valid request
+     *
+     * @param $valid_token
+     */
+    private function handlevalidresponse($valid_token){
+        $response = $this->fetchResponse($valid_token);
+        $this->assertTrue((bool) $response->getHeader('Content-Type'));
+        $this->assertTrue((bool) ($response->getContent()));
+
+        $status_code = $response->getStatusCode();
+        $this->assertTrue(is_null($status_code) || (200 == $status_code));
+    }
+
+    /**
+     *  modified image request with invalid token
+     *  expect: 412 status code
+     */
+    function test_invalid_token() {
+        $invalid_tokens = array(
+            'invalid_token_wrongid' => media_get_token('junk', 200, 100),
+            'invalid_token_wrongh'  => media_get_token($this->media, 200, 10),
+            'invalid_token_wrongw'  => media_get_token($this->media, 20, 100),
+            'invalid_token_wrongwh' => media_get_token($this->media, 20, 10)
+        );
+        foreach($invalid_tokens as $invalid_token)
+            $this->assertEquals(412, $this->fetchResponse('tok='.$invalid_token.'&')->getStatusCode());
+
+    }
+
+    /**
+     *  modified image request with no token
+     *  and not modified image with no token
+     *  expect: 412 status code
+     */
+    function test_missing_token() {
+        $no_token = '';
+
+        $this->assertEquals(412, $this->fetchResponse($no_token)->getStatusCode());
+
+        $this->width = $this->height = 0;
+        $this->assertEquals(412, $this->fetchResponse($no_token)->getStatusCode());
+    }
+}
+//Setup VIM: ex: et ts=4 :
diff --git a/inc/common.php b/inc/common.php
index bff6e80de214fdd4af9bdb95b41db9fd5db12d8d..3312141c8362d472e82d6e9743664b5fcfe9d589 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -148,7 +148,7 @@ function pageinfo() {
     $info['id']  = $ID;
     $info['rev'] = $REV;
 
-    if(isset($_SERVER['REMOTE_USER'])) {
+    if(isset($_SERVER['REMOTE_USER'])) {
         $sub = new Subscription();
         $info['subscribed'] = $sub->user_subscription();
     } else {
@@ -474,7 +474,7 @@ function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false)
 
     if(is_array($more)) {
         // add token for resized images
-        if($more['w'] || $more['h']){
+        if($more['w'] || $more['h'] || $isexternalimage){
             $more['tok'] = media_get_token($id,$more['w'],$more['h']);
         }
         // strip defaults for shorter URLs
@@ -485,12 +485,13 @@ function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false)
         $more = buildURLparams($more, $sep);
     } else {
         $matches = array();
-        if (preg_match_all('/\b(w|h)=(\d*)\b/',$more,$matches,PREG_SET_ORDER)){
+        if (preg_match_all('/\b(w|h)=(\d*)\b/',$more,$matches,PREG_SET_ORDER) || $isexternalimage){
             $resize = array('w'=>0, 'h'=>0);
             foreach ($matches as $match){
                 $resize[$match[1]] = $match[2];
             }
-            $more .= $sep.'tok='.media_get_token($id,$resize['w'],$resize['h']);
+            $more .= $more === '' ? '' : $sep;
+            $more .= 'tok='.media_get_token($id,$resize['w'],$resize['h']);
         }
         $more = str_replace('cache=cache', '', $more); //skip default
         $more = str_replace(',,', ',', $more);
@@ -506,14 +507,8 @@ function ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false)
     // external URLs are always direct without rewriting
     if($isexternalimage) {
         $xlink .= 'lib/exe/fetch.php';
-        // add hash:
-        $xlink .= '?hash='.substr(PassHash::hmac('md5', $id, auth_cookiesalt()), 0, 6);
-        if($more) {
-            $xlink .= $sep.$more;
-            $xlink .= $sep.'media='.rawurlencode($id);
-        } else {
-            $xlink .= $sep.'media='.rawurlencode($id);
-        }
+        $xlink .= '?'.$more;
+        $xlink .= $sep.'media='.rawurlencode($id);
         return $xlink;
     }
 
diff --git a/inc/fetch.functions.php b/inc/fetch.functions.php
index 53ade3555f78d79042b620ce137d1f9af5cacb9f..207ad9e5face6ba2a18bd35bef5c530f930beed4 100644
--- a/inc/fetch.functions.php
+++ b/inc/fetch.functions.php
@@ -101,8 +101,8 @@ function checkFileStatus(&$media, &$file, $rev = '', $width=0, $height=0) {
 
     //media to local file
     if(media_isexternal($media)) {
-        //check hash
-        if(substr(PassHash::hmac('md5', $media, auth_cookiesalt()), 0, 6) !== $INPUT->str('hash')) {
+        //check token for external image and additional for resized and cached images
+        if(media_get_token($media, $width, $height) !== $INPUT->str('tok')) {
             return array(412, 'Precondition Failed');
         }
         //handle external images
diff --git a/inc/media.php b/inc/media.php
index c76f2986cf2365780dacdf75b9f1d829a9f7c9a2..c4378fe9e5c51af9130ecdee5926b831971ca9b5 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -1900,7 +1900,7 @@ function media_crop_image($file, $ext, $w, $h=0){
  */
 function media_get_token($id,$w,$h){
     // token is only required for modified images
-    if ($w || $h) {
+    if ($w || $h || media_isexternal($id)) {
         $token = $id;
         if ($w) $token .= '.'.$w;
         if ($h) $token .= '.'.$h;
diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php
index 7a225037396cea8472e0a966aeb6bc7f47501185..5967494bfd24b0179f8a8a30d19a80846212dec5 100644
--- a/lib/exe/fetch.php
+++ b/lib/exe/fetch.php
@@ -60,6 +60,7 @@ if (defined('SIMPLE_TEST')) {
     if($evt->advise_before()) {
         // redirects
         if($data['status'] > 300 && $data['status'] <= 304) {
+            if (defined('SIMPLE_TEST')) return; //TestResponse doesn't recognize redirects
             send_redirect($data['statusmessage']);
         }
         // send any non 200 status