diff --git a/changelog.php b/changelog.php
index e1c9133..a44759d 100644
--- a/changelog.php
+++ b/changelog.php
@@ -54,6 +54,7 @@ if (user_logged_in()) {
 <h1>Changelog</h1>
 <?php
 $cache = new Cache('engine/cache/changelog');
+$cache->useMemory(false);
 if ($updateCache === true) {
 	$changelogs = mysql_select_multi("SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC;");
 
diff --git a/config.php b/config.php
index 147ccac..bc455bf 100644
--- a/config.php
+++ b/config.php
@@ -658,8 +658,15 @@
 		'name' => 'Forgotten' // Must be identical to config.lua (OT config file) server name.
 	);
 
-	// How often do you want highscores to update?
-	$config['cache_lifespan'] = 5; // 60 * 15; // 15 minutes.
+	// How often do you want highscores (cache) to update?
+	$config['cache'] = array(
+		// If you have two instances installed on same server, make each instance prefix unique
+		'prefix' => 'znote_',
+		// 60 * 15; // 15 minutes.
+		'lifespan' => 5,
+		// Store cache in memory/RAM? Requires PHP extension APCu
+		'memory' => true
+	);
 
 	// WARNING! Account names written here will have admin access to web page!
 	$config['page_admin_access'] = array(
diff --git a/engine/function/cache.php b/engine/function/cache.php
index 7c140bb..7a48603 100644
--- a/engine/function/cache.php
+++ b/engine/function/cache.php
@@ -1,122 +1,167 @@
 <?php
 
-	class Cache
-	{
-		protected $_file = false;
-		protected $_lifespan = 0;
-		protected $_content;
+class Cache
+{
+	protected $_file = false;
+	protected $_lifespan = 0;
+	protected $_content;
+	protected $_memory = false;
+	protected $_canMemory = false;
 
-		const EXT = '.cache.php';
+	const EXT = '.cache.php';
 
 
-		/**
-		 * @param  string $file
-		 * @access public
-		 * @return void
-		**/
-		public function __construct($file) {
-			$this->_file = $file . self::EXT;
-			$this->setExpiration(config('cache_lifespan'));
+	/**
+	 * @param  string $file
+	 * @access public
+	 * @return void
+	**/
+	public function __construct($file) {
+		$cfg = config('cache');
+
+		$this->setExpiration($cfg['lifespan']);
+		if (function_exists('apcu_fetch')) {
+			$this->_canMemory = true;
+			$this->_memory = $cfg['memory'];
 		}
+		$this->_file = $file . self::EXT;
+
+		if (!$this->_memory && $cfg['memory']) die("
+			<p><strong>Configuration error!</strong>
+			<br>Cannot save cache to memory, but it is configured to do so.
+			<br>You need to enable PHP extension APCu to enable memory cache.
+			<br>Install it or set \$config['cache']['memory'] to false!
+			<br><strong>Ubuntu install:</strong> sudo apt install php-apcu</p>
+		");
+	}
 
 
-		/**
-		 * Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!).
-		 *
-		 * @param  integer $span
-		 * @access public
-		 * @return void
-		**/
-		public function setExpiration($span) {
-			$this->_lifespan = $span;
-		}
+	/**
+	 * Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!).
+	 *
+	 * @param  integer $span
+	 * @access public
+	 * @return void
+	**/
+	public function setExpiration($span) {
+		$this->_lifespan = $span;
+	}
 
 
-		/**
-		 * Set the content you'd like to cache.
-		 *
-		 * @param  mixed $content
-		 * @access public
-		 * @return void
-		**/
-		public function setContent($content) {
-			switch (strtolower(gettype($content))) {
-				case 'array':
-					$this->_content = json_encode($content);
-					break;
-
-				default:
-					$this->_content = $content;
-					break;
-			}
-		}
-
-
-		/**
-		 * Validates whether it is time to refresh the cache data or not.
-		 *
-		 * @access public
-		 * @return boolean
-		**/
-		public function hasExpired() {
-			if (is_file($this->_file) && time() < filemtime($this->_file) + $this->_lifespan) {
-				return false;
-			}
-
+	/**
+	 * Enable or disable memory RAM storage.
+	 *
+	 * @param  bool $bool
+	 * @access public
+	 * @return bool $status
+	**/
+	public function useMemory($bool) {
+		if ($bool and $this->_canMemory) {
+			$this->_memory = true;
 			return true;
 		}
+		$this->_memory = false;
+		return false;
+	}
 
-		/**
-		 * Returns remaining time before scoreboard will update itself.
-		 *
-		 * @access public
-		 * @return integer
-		**/
-		public function remainingTime() {
-			$remaining = 0;
-			if (!$this->hasExpired()) {
-				$remaining = (filemtime($this->_file) + $this->_lifespan) - time();
+
+	/**
+	 * Set the content you'd like to cache.
+	 *
+	 * @param  mixed $content
+	 * @access public
+	 * @return void
+	**/
+	public function setContent($content) {
+		$this->_content = (!$this->_memory && strtolower(gettype($content)) == 'array') ? json_encode($content) : $content;
+	}
+
+
+	/**
+	 * Validates whether it is time to refresh the cache data or not.
+	 *
+	 * @access public
+	 * @return boolean
+	**/
+	public function hasExpired() {
+		if ($this->_memory) {
+			return !apcu_exists($this->_file);
+		}
+		if (is_file($this->_file) && time() < filemtime($this->_file) + $this->_lifespan) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Returns remaining time before scoreboard will update itself.
+	 *
+	 * @access public
+	 * @return integer
+	**/
+	public function remainingTime() {
+		$remaining = 0;
+		if ($this->_memory) {
+			if (apcu_exists($this->_file)) {
+				$meta = apc_cache_info('user');
+				foreach ($meta['cache_list'] AS $item) {
+					if ($item['info'] == $this->_file) {
+						$remaining = ($item['creation_time'] + $item['ttl']) - time();
+						return ($remaining > 0) ? $remaining : 0;
+					}
+				}
 			}
 			return $remaining;
 		}
+		if (!$this->hasExpired()) {
+			$remaining = (filemtime($this->_file) + $this->_lifespan) - time();
+		}
+		return $remaining;
+	}
 
 
-		/**
-		 * Saves the content into its appropriate cache file.
-		 *
-		 * @access public
-		 * @return void
-		**/
-		public function save() {
-			$handle = fopen($this->_file, 'w');
-			fwrite($handle, $this->_content);
-			fclose($handle);
+	/**
+	 * Saves the content into its appropriate cache file.
+	 *
+	 * @access public
+	 * @return void
+	**/
+	public function save() {
+		if ($this->_memory) {
+			return apcu_store($this->_file, $this->_content, $this->_lifespan);
+		}
+		$handle = fopen($this->_file, 'w');
+		fwrite($handle, $this->_content);
+		fclose($handle);
+	}
+
+
+	/**
+	 * Loads the content from a specified cache file.
+	 *
+	 * @access public
+	 * @return mixed
+	**/
+	public function load() {
+		if ($this->_memory) {
+			return apcu_fetch($this->_file);
+		}
+		if (!is_file($this->_file)) {
+			return false;
 		}
 
+		ob_start();
+		include_once($this->_file);
+		$content = ob_get_clean();
 
-		/**
-		 * Loads the content from a specified cache file.
-		 *
-		 * @access public
-		 * @return mixed
-		**/
-		public function load() {
-			if (!is_file($this->_file)) {
-				return false;
-			}
+		if (!isset($content) && strlen($content) == 0) {
+			return false;
+		}
 
-			ob_start();
-			include_once($this->_file);
-			$content = ob_get_clean();
-
-			if (!isset($content) && strlen($content) == 0) {
-				return false;
-			}
-
-			if ($content = json_decode($content, true)) {
-				return (array) $content;
-			} else {
-				return $content;
-			}
+		if ($content = json_decode($content, true)) {
+			return (array) $content;
+		} else {
+			return $content;
 		}
 	}
+}
diff --git a/index.php b/index.php
index c42be58..bb80f59 100644
--- a/index.php
+++ b/index.php
@@ -15,6 +15,7 @@ require_once 'engine/init.php'; include 'layout/overall/header.php';
 			// Changelog ticker //
 			// Load from cache
 			$changelogCache = new Cache('engine/cache/changelog');
+			$changelogCache->useMemory(false);
 			$changelogs = $changelogCache->load();
 
 			if (isset($changelogs) && !empty($changelogs) && $changelogs !== false) {
diff --git a/layout/sub/index.php b/layout/sub/index.php
index e3482da..da31421 100644
--- a/layout/sub/index.php
+++ b/layout/sub/index.php
@@ -4,6 +4,7 @@ if ($config['UseChangelogTicker']) {
 	// Changelog ticker //
 	// Load from cache
 	$changelogCache = new Cache('engine/cache/changelog');
+	$changelogCache->useMemory(false);
 	$changelogs = $changelogCache->load();
 
 	if (isset($changelogs) && !empty($changelogs) && $changelogs !== false) {