diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index dd03b0c895a0915fe53bfacff5ec3fbf27aa12d2..81dfea2114d138fd93cd264a07e59f371195d134 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -1433,13 +1433,6 @@
 			delete this._reloadCall;
 			this.hideMask();
 
-			if (status === 401) {
-				// TODO: append current URL to be able to get back after logging in again
-				OC.redirect(OC.generateUrl('apps/files'));
-				OC.Notification.show(result);
-				return false;
-			}
-
 			// Firewall Blocked request?
 			if (status === 403) {
 				// Go home
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index ed56011866ec8b01e4e0b901509bf2107ff82d4e..a83c8c4c0bc6c56272e7e44f2b88407001b9267d 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -2462,13 +2462,6 @@ describe('OCA.Files.FileList tests', function() {
 			getFolderContentsStub.restore();
 			fileList = undefined;
 		});
-		it('redirects to files app in case of auth error', function () {
-			deferredList.reject(401, 'Authentication error');
-
-			expect(redirectStub.calledOnce).toEqual(true);
-			expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files');
-			expect(getFolderContentsStub.calledOnce).toEqual(true);
-		});
 		it('redirects to root folder in case of forbidden access', function () {
 			deferredList.reject(403);
 
diff --git a/core/js/files/client.js b/core/js/files/client.js
index 627630e8b0382d2a140f75307978abd135ea9e9c..a7f393d325fd56d0879e900812ef7c39eb9588a9 100644
--- a/core/js/files/client.js
+++ b/core/js/files/client.js
@@ -137,6 +137,8 @@
 				});
 				return result;
 			};
+
+			OC.registerXHRForErrorProcessing(xhr);
 			return xhr;
 		},
 
diff --git a/core/js/js.js b/core/js/js.js
index 83658a537b8fe5254cdef430a0cd2a1299b61e74..fac9c45f6687ada5303896562c6b60333161df60 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -234,6 +234,13 @@ var OC={
 		window.location = targetURL;
 	},
 
+	/**
+	 * Reloads the current page
+	 */
+	reload: function() {
+		window.location.reload();
+	},
+
 	/**
 	 * Protocol that is used to access this ownCloud instance
 	 * @return {string} Used protocol
@@ -727,6 +734,56 @@ var OC={
 	isUserAdmin: function() {
 		return oc_isadmin;
 	},
+
+	/**
+	 * Process ajax error, redirects to main page
+	 * if an error/auth error status was returned.
+	 */
+	_processAjaxError: function(xhr) {
+		// purposefully aborted request ?
+		if (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout')) {
+			return;
+		}
+
+		if (_.contains([0, 302, 307, 401], xhr.status)) {
+			OC.reload();
+		}
+	},
+
+	/**
+	 * Registers XmlHttpRequest object for global error processing.
+	 *
+	 * This means that if this XHR object returns 401 or session timeout errors,
+	 * the current page will automatically be reloaded.
+	 *
+	 * @param {XMLHttpRequest} xhr
+	 */
+	registerXHRForErrorProcessing: function(xhr) {
+		var loadCallback = function() {
+			if (xhr.readyState !== 4) {
+				return;
+			}
+
+			if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
+				return;
+			}
+
+			// fire jquery global ajax error handler
+			$(document).trigger(new $.Event('ajaxError'), xhr);
+		};
+
+		var errorCallback = function() {
+			// fire jquery global ajax error handler
+			$(document).trigger(new $.Event('ajaxError'), xhr);
+		};
+
+		// FIXME: also needs an IE8 way
+		if (xhr.addEventListener) {
+			xhr.addEventListener('load', loadCallback);
+			xhr.addEventListener('error', errorCallback);
+		}
+
+	}
 };
 
 /**
@@ -1311,6 +1368,13 @@ function initCore() {
 		$('html').addClass('edge');
 	}
 
+	$(document).on('ajaxError.main', function( event, request, settings ) {
+		if (settings && settings.allowAuthErrors) {
+			return;
+		}
+		OC._processAjaxError(request);
+	});
+
 	/**
 	 * Calls the server periodically to ensure that session doesn't
 	 * time out
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index de41b66ec329466d39a8b871c81fc34d05e1ae33..1819b5a9c1ecd7db44158ab644454943f5b57d28 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -40,7 +40,8 @@
 						'<d:propfind xmlns:d="DAV:">' +
 						'<d:prop><d:resourcetype/></d:prop>' +
 						'</d:propfind>',
-				complete: afterCall
+				complete: afterCall,
+				allowAuthErrors: true
 			});
 			return deferred.promise();
 		},
@@ -157,7 +158,8 @@
 
 			$.ajax({
 				type: 'GET',
-				url: OC.generateUrl('settings/ajax/checksetup')
+				url: OC.generateUrl('settings/ajax/checksetup'),
+				allowAuthErrors: true
 			}).then(afterCall, afterCall);
 			return deferred.promise();
 		},
@@ -181,7 +183,8 @@
 
 			$.ajax({
 				type: 'GET',
-				url: OC.generateUrl('heartbeat')
+				url: OC.generateUrl('heartbeat'),
+				allowAuthErrors: true
 			}).then(afterCall, afterCall);
 
 			return deferred.promise();
diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js
index d13691845a7d4ec9a0d3724d58691e7e43bfac46..f9bdeae0d64bfef35f5246d93cc58adf90e29e8c 100644
--- a/core/js/tests/specHelper.js
+++ b/core/js/tests/specHelper.js
@@ -116,7 +116,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
 // global setup for all tests
 (function setupTests() {
 	var fakeServer = null,
-		$testArea = null;
+		$testArea = null,
+		ajaxErrorStub = null;
 
 	/**
 	 * Utility functions for testing
@@ -162,6 +163,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
 
 		// dummy select2 (which isn't loaded during the tests)
 		$.fn.select2 = function() { return this; };
+
+		ajaxErrorStub = sinon.stub(OC, '_processAjaxError');
 	});
 
 	afterEach(function() {
@@ -172,6 +175,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
 		$testArea.remove();
 
 		delete($.fn.select2);
+
+		ajaxErrorStub.restore();
 	});
 })();
 
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index 2e970f7e7071b664e4e1690934658a18a5af458d..32eb8df32d12d6ef6c5bb99de5b30aac78c5074d 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -302,6 +302,7 @@ describe('Core base tests', function() {
 			/* jshint camelcase: false */
 			window.oc_config = oldConfig;
 			routeStub.restore();
+			$(document).off('ajaxError');
 		});
 		it('sends heartbeat half the session lifetime when heartbeat enabled', function() {
 			/* jshint camelcase: false */
@@ -473,6 +474,7 @@ describe('Core base tests', function() {
 		});
 		afterEach(function() {
 			clock.restore();
+			$(document).off('ajaxError');
 		});
 		it('Sets up menu toggle', function() {
 			window.initCore();
@@ -841,5 +843,45 @@ describe('Core base tests', function() {
 			// verification is done in afterEach
 		});
 	});
+	describe('global ajax errors', function() {
+		var reloadStub, ajaxErrorStub;
+
+		beforeEach(function() {
+			reloadStub = sinon.stub(OC, 'reload');
+			// unstub the error processing method
+			ajaxErrorStub = OC._processAjaxError;
+			ajaxErrorStub.restore();
+			window.initCore();
+		});
+		afterEach(function() {
+			reloadStub.restore();
+			$(document).off('ajaxError');
+		});
+
+		it('reloads current page in case of auth error', function () {
+			var dataProvider = [
+				[200, false],
+				[400, false],
+				[401, true],
+				[302, true],
+				[307, true]
+			];
+
+			for (var i = 0; i < dataProvider.length; i++) {
+				var xhr = { status: dataProvider[i][0] };
+				var expectedCall = dataProvider[i][1];
+
+				reloadStub.reset();
+
+				$(document).trigger(new $.Event('ajaxError'), xhr);
+
+				if (expectedCall) {
+					expect(reloadStub.calledOnce).toEqual(true);
+				} else {
+					expect(reloadStub.notCalled).toEqual(true);
+				}
+			}
+		});
+	})
 });
 
diff --git a/lib/private/api.php b/lib/private/api.php
index 452612d4c16e90fe8e3fb46e6ad3d0b3c6f0ff01..6c6be233c9dfbcb9310543aae2ef34219a12c4a3 100644
--- a/lib/private/api.php
+++ b/lib/private/api.php
@@ -377,9 +377,16 @@ class OC_API {
 	 * @param string $format the format xml|json
 	 */
 	public static function respond($result, $format='xml') {
+		$request = \OC::$server->getRequest();
+
 		// Send 401 headers if unauthorised
 		if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) {
-			header('WWW-Authenticate: Basic realm="Authorisation Required"');
+			// If request comes from JS return dummy auth request
+			if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
+				header('WWW-Authenticate: DummyBasic realm="Authorisation Required"');
+			} else {
+				header('WWW-Authenticate: Basic realm="Authorisation Required"');
+			}
 			header('HTTP/1.0 401 Unauthorized');
 		}
 
@@ -389,7 +396,7 @@ class OC_API {
 
 		$meta = $result->getMeta();
 		$data = $result->getData();
-		if (self::isV2(\OC::$server->getRequest())) {
+		if (self::isV2($request)) {
 			$statusCode = self::mapStatusCodes($result->getStatusCode());
 			if (!is_null($statusCode)) {
 				$meta['statuscode'] = $statusCode;
diff --git a/lib/private/json.php b/lib/private/json.php
index adee28a1593c9de9681bfad268ce396ac5b42029..74aebd476fb2e8229ef6b808428eede92c084971 100644
--- a/lib/private/json.php
+++ b/lib/private/json.php
@@ -66,6 +66,7 @@ class OC_JSON{
 	public static function checkLoggedIn() {
 		if( !OC_User::isLoggedIn()) {
 			$l = \OC::$server->getL10N('lib');
+			http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED);
 			self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' )));
 			exit();
 		}