diff --git a/.gitignore b/.gitignore
index de53251..8e7176a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ pageindex.json
 idindex.json
 # The search index
 invindex.json
+# The recent changes list
+recent-changes.json
 
 # All uploaded files
 build/Files/*
diff --git a/Changelog.md b/Changelog.md
index 2ddc6f0..fe470f2 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,11 +2,16 @@
 
 ## v0.10
 ## Added
- - 
+ - This changelog. It's long overdue I think!
+ - Added the all tags page to the "More..." menu by default.
 
 ## Changed
  - Improved appearance of the all pages list.
  - Improved apparence of the tag list page.
- - Added the all tags page to the "More..." menu by default.
  - Added a link back to the list of tags on the list of pages with a particular tag.
- - Upgrade help page. Modules can now register their own sections on a wiki's help page.
+ - Upgraded help page. Modules can now register their own sections on a wiki's help page.
+ - Optimised search queries a bit.
+ - Save preprocessors now get passed an extra parameter, which contains the old page source.
+
+## Fixed
+ - Removed debug statement from the redirect page module.
diff --git a/build/index.php b/build/index.php
index 606ddb1..3421736 100644
--- a/build/index.php
+++ b/build/index.php
@@ -94,6 +94,9 @@ $settings->show_subpages = true;
 // the page.
 $settings->subpages_display_depth = 3;
 
+// The maximum number of recent changes to display on the recent changes page.
+$settings->max_recent_changes = 512;
+
 // An array of usernames and passwords - passwords should be hashed with
 // sha256. Put one user / password on each line, remembering the comma at the
 // end. The last user in the list doesn't need a comma after their details though.
@@ -1529,9 +1532,93 @@ function render_sidebar($pageindex, $root_pagename = "")
 
 
 
+register_module([
+	"name" => "Recent Changes",
+	"version" => "0.1",
+	"author" => "Starbeamrainbowlabs",
+	"description" => "Adds recent changes. Access through the 'recent-changes' action.",
+	"id" => "feature-recent-changes",
+	"code" => function() {
+		global $settings, $env, $paths;
+		// Add the recent changes json file to $paths for convenience.
+		$paths->recentchanges = $env->storage_prefix . "recent-changes.json";
+		// Create the recent changes json file if it doesn't exist
+		if(!file_exists($paths->recentchanges))
+			file_put_contents($paths->recentchanges, "[]");
+		
+		/*
+		 * ██████  ███████  ██████ ███████ ███    ██ ████████         
+		 * ██   ██ ██      ██      ██      ████   ██    ██            
+		 * ██████  █████   ██      █████   ██ ██  ██    ██            
+		 * ██   ██ ██      ██      ██      ██  ██ ██    ██            
+		 * ██   ██ ███████  ██████ ███████ ██   ████    ██            
+		 * 
+		 *  ██████ ██   ██  █████  ███    ██  ██████  ███████ ███████ 
+		 * ██      ██   ██ ██   ██ ████   ██ ██       ██      ██      
+		 * ██      ███████ ███████ ██ ██  ██ ██   ███ █████   ███████ 
+		 * ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██           ██ 
+		 *  ██████ ██   ██ ██   ██ ██   ████  ██████  ███████ ███████
+		 */
+		add_action("recent-changes", function() {
+			global $settings, $paths;
+			
+			$content = "\t\t<h1>Recent Changes</h1>
+		<ul>\n";
+			
+			$recentchanges = json_decode(file_get_contents($paths->recentchanges));
+			foreach($recentchanges as $rchange)
+			{
+				// The number (and the sign) of the size difference to display
+				$size_display = ($rchange->sizediff > 0 ? "+" : ($rchange < 0 ? "-" : "")) . $rchange->sizediff;
+				$size_display_class = $rchange->sizediff > 0 ? "larger" : ($rchange < 0 ? "smaller" : "nochange");
+				$content .= "\t\t\t<li><span class='editor'>&#9998; $rchange->page $rchange->user</span> " . human_time_since($rchange->timestamp) . " <span class='$size_display_class' title='New size: $rchange->newsize'>($size_display)</span></li>\n";
+			}
+			
+			$content .= "\t\t</ul>";
+			
+			echo(page_renderer::render("Recent Changes - $settings->sitename", $content));
+		});
+		
+		register_save_preprocessor(function(&$pageinfo, &$newsource, &$oldsource) {
+			global $env, $settings, $paths;
+			
+			// Work out the old and new page lengths
+			$oldsize = strlen($oldsource);
+			$newsize = strlen($newsource);
+			// Calculate the page length difference
+			$size_diff = $newsize - $oldsize;
+			
+			error_log("$oldsize -> $newsize");
+			error_log("Size diff: $size_diff");
+			
+			$recentchanges = json_decode(file_get_contents($paths->recentchanges), true);
+			$recentchanges[] = [
+				"timestamp" => time(),
+				"page" => $env->page,
+				"user" => $env->user,
+				"newsize" => $newsize,
+				"sizediff" => $size_diff
+			];
+			
+			// Limit the number of entries in the recent changes file if we've
+			// been asked to.
+			if(isset($settings->max_recent_changes))
+				$recentchanges = array_slice($recentchanges, -$settings->max_recent_changes);
+			
+			// Save the recent changes file back to disk
+			file_put_contents($paths->recentchanges, json_encode($recentchanges, JSON_PRETTY_PRINT));
+		});
+		
+		add_help_section("800-raw-page-content", "Recent Changes", "<p></p>");
+	}
+]);
+
+
+
+
 register_module([
 	"name" => "Redirect pages",
-	"version" => "0.2",
+	"version" => "0.3",
 	"author" => "Starbeamrainbowlabs",
 	"description" => "Adds support for redirect pages. Uses the same syntax that Mediawiki does.",
 	"id" => "feature-redirect",
@@ -1542,7 +1629,7 @@ register_module([
 			$matches = [];
 			if(preg_match("/^# ?REDIRECT ?\[\[([^\]]+)\]\]/i", $pagedata, $matches) === 1)
 			{
-				error_log("matches: " . var_export($matches, true));
+				//error_log("matches: " . var_export($matches, true));
 				// We have found a redirect page!
 				// Update the metadata to reflect this.
 				$index_entry->redirect = true;
@@ -2751,8 +2838,12 @@ register_module([
 			
 			// Construct an index for the old and new page content
 			$oldindex = [];
+			$oldpagedata = ""; // We need the old page data in order to pass it to the preprocessor
 			if(file_exists("$env->page.md"))
-				$oldindex = search::index(file_get_contents("$env->page.md"));
+			{
+				$oldpagedata = file_get_contents("$env->page.md");
+				$oldindex = search::index($oldpagedata);
+			}
 			$newindex = search::index($pagedata);
 			
 			// Compare the indexes of the old and new content
@@ -2796,7 +2887,7 @@ register_module([
 				// Execute all the preprocessors
 				foreach($save_preprocessors as $func)
 				{
-					$func($pageindex->$page, $pagedata);
+					$func($pageindex->$page, $pagedata, $oldpagedata);
 				}
 				
 				if($pagedata !== $pagedata_orig)
diff --git a/module_index.json b/module_index.json
index 6070718..5eb0176 100644
--- a/module_index.json
+++ b/module_index.json
@@ -35,13 +35,22 @@
         "lastupdate": 1450704211,
         "optional": false
     },
+    {
+        "name": "Recent Changes",
+        "version": "0.1",
+        "author": "Starbeamrainbowlabs",
+        "description": "Adds recent changes. Access through the 'recent-changes' action.",
+        "id": "feature-recent-changes",
+        "lastupdate": 1452950460,
+        "optional": false
+    },
     {
         "name": "Redirect pages",
-        "version": "0.2",
+        "version": "0.3",
         "author": "Starbeamrainbowlabs",
         "description": "Adds support for redirect pages. Uses the same syntax that Mediawiki does.",
         "id": "feature-redirect",
-        "lastupdate": 1450686571,
+        "lastupdate": 1452949822,
         "optional": false
     },
     {
@@ -86,7 +95,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
         "id": "page-edit",
-        "lastupdate": 1450687286,
+        "lastupdate": 1452949641,
         "optional": false
     },
     {
diff --git a/modules/feature-recent-changes.php b/modules/feature-recent-changes.php
new file mode 100644
index 0000000..c7726d4
--- /dev/null
+++ b/modules/feature-recent-changes.php
@@ -0,0 +1,83 @@
+<?php
+register_module([
+	"name" => "Recent Changes",
+	"version" => "0.1",
+	"author" => "Starbeamrainbowlabs",
+	"description" => "Adds recent changes. Access through the 'recent-changes' action.",
+	"id" => "feature-recent-changes",
+	"code" => function() {
+		global $settings, $env, $paths;
+		// Add the recent changes json file to $paths for convenience.
+		$paths->recentchanges = $env->storage_prefix . "recent-changes.json";
+		// Create the recent changes json file if it doesn't exist
+		if(!file_exists($paths->recentchanges))
+			file_put_contents($paths->recentchanges, "[]");
+		
+		/*
+		 * ██████  ███████  ██████ ███████ ███    ██ ████████         
+		 * ██   ██ ██      ██      ██      ████   ██    ██            
+		 * ██████  █████   ██      █████   ██ ██  ██    ██            
+		 * ██   ██ ██      ██      ██      ██  ██ ██    ██            
+		 * ██   ██ ███████  ██████ ███████ ██   ████    ██            
+		 * 
+		 *  ██████ ██   ██  █████  ███    ██  ██████  ███████ ███████ 
+		 * ██      ██   ██ ██   ██ ████   ██ ██       ██      ██      
+		 * ██      ███████ ███████ ██ ██  ██ ██   ███ █████   ███████ 
+		 * ██      ██   ██ ██   ██ ██  ██ ██ ██    ██ ██           ██ 
+		 *  ██████ ██   ██ ██   ██ ██   ████  ██████  ███████ ███████
+		 */
+		add_action("recent-changes", function() {
+			global $settings, $paths;
+			
+			$content = "\t\t<h1>Recent Changes</h1>
+		<ul>\n";
+			
+			$recentchanges = json_decode(file_get_contents($paths->recentchanges));
+			foreach($recentchanges as $rchange)
+			{
+				// The number (and the sign) of the size difference to display
+				$size_display = ($rchange->sizediff > 0 ? "+" : ($rchange < 0 ? "-" : "")) . $rchange->sizediff;
+				$size_display_class = $rchange->sizediff > 0 ? "larger" : ($rchange < 0 ? "smaller" : "nochange");
+				$content .= "\t\t\t<li><span class='editor'>&#9998; $rchange->page $rchange->user</span> " . human_time_since($rchange->timestamp) . " <span class='$size_display_class' title='New size: $rchange->newsize'>($size_display)</span></li>\n";
+			}
+			
+			$content .= "\t\t</ul>";
+			
+			echo(page_renderer::render("Recent Changes - $settings->sitename", $content));
+		});
+		
+		register_save_preprocessor(function(&$pageinfo, &$newsource, &$oldsource) {
+			global $env, $settings, $paths;
+			
+			// Work out the old and new page lengths
+			$oldsize = strlen($oldsource);
+			$newsize = strlen($newsource);
+			// Calculate the page length difference
+			$size_diff = $newsize - $oldsize;
+			
+			error_log("$oldsize -> $newsize");
+			error_log("Size diff: $size_diff");
+			
+			$recentchanges = json_decode(file_get_contents($paths->recentchanges), true);
+			$recentchanges[] = [
+				"timestamp" => time(),
+				"page" => $env->page,
+				"user" => $env->user,
+				"newsize" => $newsize,
+				"sizediff" => $size_diff
+			];
+			
+			// Limit the number of entries in the recent changes file if we've
+			// been asked to.
+			if(isset($settings->max_recent_changes))
+				$recentchanges = array_slice($recentchanges, -$settings->max_recent_changes);
+			
+			// Save the recent changes file back to disk
+			file_put_contents($paths->recentchanges, json_encode($recentchanges, JSON_PRETTY_PRINT));
+		});
+		
+		add_help_section("800-raw-page-content", "Recent Changes", "<p></p>");
+	}
+]);
+
+?>
diff --git a/modules/feature-redirect.php b/modules/feature-redirect.php
index a83b207..f7fd2f9 100644
--- a/modules/feature-redirect.php
+++ b/modules/feature-redirect.php
@@ -1,7 +1,7 @@
 <?php
 register_module([
 	"name" => "Redirect pages",
-	"version" => "0.2",
+	"version" => "0.3",
 	"author" => "Starbeamrainbowlabs",
 	"description" => "Adds support for redirect pages. Uses the same syntax that Mediawiki does.",
 	"id" => "feature-redirect",
@@ -12,7 +12,7 @@ register_module([
 			$matches = [];
 			if(preg_match("/^# ?REDIRECT ?\[\[([^\]]+)\]\]/i", $pagedata, $matches) === 1)
 			{
-				error_log("matches: " . var_export($matches, true));
+				//error_log("matches: " . var_export($matches, true));
 				// We have found a redirect page!
 				// Update the metadata to reflect this.
 				$index_entry->redirect = true;
diff --git a/modules/page-edit.php b/modules/page-edit.php
index 8f73693..ad0df25 100644
--- a/modules/page-edit.php
+++ b/modules/page-edit.php
@@ -141,8 +141,12 @@ register_module([
 			
 			// Construct an index for the old and new page content
 			$oldindex = [];
+			$oldpagedata = ""; // We need the old page data in order to pass it to the preprocessor
 			if(file_exists("$env->page.md"))
-				$oldindex = search::index(file_get_contents("$env->page.md"));
+			{
+				$oldpagedata = file_get_contents("$env->page.md");
+				$oldindex = search::index($oldpagedata);
+			}
 			$newindex = search::index($pagedata);
 			
 			// Compare the indexes of the old and new content
@@ -186,7 +190,7 @@ register_module([
 				// Execute all the preprocessors
 				foreach($save_preprocessors as $func)
 				{
-					$func($pageindex->$page, $pagedata);
+					$func($pageindex->$page, $pagedata, $oldpagedata);
 				}
 				
 				if($pagedata !== $pagedata_orig)
diff --git a/settings.fragment.php b/settings.fragment.php
index c7e44a9..3c44586 100644
--- a/settings.fragment.php
+++ b/settings.fragment.php
@@ -83,6 +83,9 @@ $settings->show_subpages = true;
 // the page.
 $settings->subpages_display_depth = 3;
 
+// The maximum number of recent changes to display on the recent changes page.
+$settings->max_recent_changes = 512;
+
 // An array of usernames and passwords - passwords should be hashed with
 // sha256. Put one user / password on each line, remembering the comma at the
 // end. The last user in the list doesn't need a comma after their details though.
