diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json
index 8fc3becbb5..7b5a6eece9 100644
--- a/public/language/en_GB/error.json
+++ b/public/language/en_GB/error.json
@@ -36,12 +36,13 @@
 
 	"topic-locked": "Topic Locked",
 
-	"still-uploading" : "Please wait for uploads to complete.",
-	"content-too-short" : "Please enter a longer post. At least %1 characters.",
-	"title-too-short" : "Please enter a longer title. At least %1 characters.",
-	"title-too-long" : "Please enter a shorter title. Titles can't be longer than %1 characters.",
-	"too-many-posts" : "You can only post every %1 seconds.",
-	"file-too-big" : "Maximum allowed file size is %1 kbs",
+	"still-uploading": "Please wait for uploads to complete.",
+	"content-too-short": "Please enter a longer post. At least %1 characters.",
+	"title-too-short": "Please enter a longer title. At least %1 characters.",
+	"title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.",
+	"invalid-title": "Invalid title!",
+	"too-many-posts": "You can only post every %1 seconds.",
+	"file-too-big": "Maximum allowed file size is %1 kbs",
 
 	"cant-vote-self-post": "You cannot vote for your own post",
 	"already-favourited": "You already favourited this post",
diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js
index d50e4b7e56..0c7234782a 100644
--- a/public/src/forum/topic.js
+++ b/public/src/forum/topic.js
@@ -47,8 +47,9 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 		addBlockquoteEllipses($('.topic .post-content > blockquote'));
 
 		var bookmark = localStorage.getItem('topic:' + tid + ':bookmark');
-		if (window.location.hash) {
-			Topic.scrollToPost(window.location.hash.substr(1), true);
+		var postIndex = getPostIndex();
+		if (postIndex) {
+			Topic.scrollToPost(postIndex, true);
 		} else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && postCount > 1) {
 			app.alert({
 				alert_id: 'bookmark',
@@ -64,9 +65,7 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 			});
 		}
 
-		if (!config.usePagination) {
-			navigator.init('.posts > .post-row', postCount, Topic.navigatorCallback);
-		}
+		navigator.init('.posts > .post-row', postCount, Topic.navigatorCallback);
 
 		socket.on('event:new_post', onNewPost);
 
@@ -78,6 +77,11 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 		socket.emit('topics.increaseViewCount', tid);
 	};
 
+	function getPostIndex() {
+		var parts = window.location.pathname.split('/');
+		return parts[4] ? (parseInt(parts[4], 10) - 1) : '';
+	}
+
 	function showBottomPostBar() {
 		if($('#post-container .post-row').length > 1 || !$('#post-container li[data-index="0"]').length) {
 			$('.bottom-post-bar').removeClass('hide');
@@ -142,78 +146,80 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 	}
 
 	Topic.navigatorCallback = function(element) {
-		var pid = element.attr('data-pid');
+		var postIndex = parseInt(element.attr('data-index'), 10);
 
 		var currentBookmark = localStorage.getItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark');
 
-		if (!currentBookmark || parseInt(pid, 10) >= parseInt(currentBookmark, 10)) {
-			localStorage.setItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark', pid);
+		if (!currentBookmark || parseInt(postIndex, 10) >= parseInt(currentBookmark, 10)) {
+			localStorage.setItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark', postIndex);
 			app.removeAlert('bookmark');
 		}
 
 		if (!scrollingToPost) {
-
-			var newUrl = window.location.href.replace(window.location.hash, '') + '#' + pid;
+			var parts = window.location.pathname.split('/');
+			var topicId = parts[2],
+				slug = parts[3];
+			var newUrl = 'topic/' + topicId + '/' + (slug ? slug : '');
+			if (postIndex > 0) {
+				 newUrl += '/' + (postIndex + 1);
+			}
 
 			if (newUrl !== currentUrl) {
 				if (history.replaceState) {
+					var search = (window.location.search ? window.location.search : '');
 					history.replaceState({
-						url: window.location.pathname.slice(1) + (window.location.search ? window.location.search : '' ) + '#' + pid
-					}, null, newUrl);
+						url: newUrl + search
+					}, null, window.location.protocol + '//' + window.location.host + '/' + newUrl + search);
 				}
 				currentUrl = newUrl;
 			}
 		}
 	};
 
-	Topic.scrollToPost = function(pid, highlight, duration, offset) {
-		if (!pid) {
+	Topic.scrollToPost = function(postIndex, highlight, duration, offset) {
+		if (!postIndex) {
 			return;
 		}
 
-		if(!offset) {
+		if (!offset) {
 			offset = 0;
 		}
 
 		scrollingToPost = true;
 
-		if($('#post_anchor_' + pid).length) {
-			return scrollToPid(pid);
+		if($('#post_anchor_' + postIndex).length) {
+			return scrollToPid(postIndex);
 		}
 
 		if(config.usePagination) {
-			socket.emit('posts.getPidPage', pid, function(err, page) {
-				if(err) {
-					return;
-				}
-				if(parseInt(page, 10) !== pagination.currentPage) {
-					pagination.loadPage(page, function() {
-						scrollToPid(pid);
-					});
-				} else {
-					scrollToPid(pid);
-				}
-			});
-		} else {
-			socket.emit('posts.getPidIndex', pid, function(err, index) {
-				if(err) {
-					return;
-				}
+			if (window.location.search.indexOf('page') !== -1) {
+				navigator.update();
+				scrollingToPost = false;
+				return;
+			}
 
-				$('#post-container').empty();
-				var after = index - config.postsPerPage + 1;
-				if(after < 0) {
-					after = 0;
-				}
+			var page = Math.ceil((postIndex + 1) / config.postsPerPage)
 
-				loadPostsAfter(after, function() {
-					scrollToPid(pid);
+			if(parseInt(page, 10) !== pagination.currentPage) {
+				pagination.loadPage(page, function() {
+					scrollToPid(postIndex);
 				});
+			} else {
+				scrollToPid(postIndex);
+			}
+		} else {
+			$('#post-container').empty();
+			var after = postIndex - config.postsPerPage + 1;
+			if(after < 0) {
+				after = 0;
+			}
+			loadPostsAfter(after, function() {
+				scrollToPid(postIndex);
 			});
 		}
 
-		function scrollToPid(pid) {
-			var scrollTo = $('#post_anchor_' + pid),
+		function scrollToPid(postIndex) {
+			var scrollTo = $('#post_anchor_' + postIndex),
 				tid = $('#post-container').attr('data-tid');
 
 			function animateScroll() {
@@ -235,9 +241,8 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 				}
 			}
 
-
 			if (tid && scrollTo.length) {
-				if($('#post-container li.post-row[data-pid="' + pid + '"]').attr('data-index') !== '0') {
+				if($('#post-container li.post-row[data-index="' + postIndex + '"]').attr('data-index') !== '0') {
 					animateScroll();
 				} else {
 					navigator.update();
@@ -362,14 +367,14 @@ define('forum/topic', ['forum/pagination', 'forum/infinitescroll', 'forum/topic/
 	}
 
 	function loadMorePosts(direction) {
-		if (!$('#post-container').length) {
+		if (!$('#post-container').length || scrollingToPost) {
 			return;
 		}
 
 		infinitescroll.calculateAfter(direction, '#post-container .post-row', config.postsPerPage, function(after, offset, el) {
 			loadPostsAfter(after, function() {
 				if (direction < 0 && el) {
-					Topic.scrollToPost(el.attr('data-pid'), false, 0, offset);
+					Topic.scrollToPost(el.attr('data-index'), false, 0, offset);
 				}
 			});
 		});
diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js
index d08f7d24c8..fa1f233c00 100644
--- a/public/src/modules/composer.js
+++ b/public/src/modules/composer.js
@@ -441,7 +441,7 @@ define('composer', ['taskbar', 'composer/controls', 'composer/uploads', 'compose
 
 		titleEl.val(titleEl.val().trim());
 		bodyEl.val(bodyEl.val().trim());
-		if(thumbEl.length) {
+		if (thumbEl.length) {
 			thumbEl.val(thumbEl.val().trim());
 		}
 
@@ -453,6 +453,8 @@ define('composer', ['taskbar', 'composer/controls', 'composer/uploads', 'compose
 			return composerAlert('[[error:title-too-short, ' + config.minimumTitleLength + ']]');
 		} else if (checkTitle && titleEl.val().length > parseInt(config.maximumTitleLength, 10)) {
 			return composerAlert('[[error:title-too-long, ' + config.maximumTitleLength + ']]');
+		} else if (checkTitle && !utils.slugify(titleEl.val()).length) {
+			return composerAlert('[[error:invalid-title]]');
 		} else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) {
 			return composerAlert('[[error:content-too-short, ' + config.minimumPostLength + ']]');
 		}
diff --git a/public/src/modules/share.js b/public/src/modules/share.js
index 2cbb16889e..41f789d826 100644
--- a/public/src/modules/share.js
+++ b/public/src/modules/share.js
@@ -10,8 +10,8 @@ define('share', function() {
 
 		var baseUrl = window.location.protocol + '//' + window.location.host;
 
-		function openShare(url, hash, width, height) {
-			window.open(url + encodeURIComponent(baseUrl + window.location.pathname + hash), '_blank', 'width=' + width + ',height=' + height + ',scrollbars=no,status=no');
+		function openShare(url, urlToPost, width, height) {
+			window.open(url + encodeURIComponent(baseUrl + urlToPost), '_blank', 'width=' + width + ',height=' + height + ',scrollbars=no,status=no');
 			return false;
 		}
 
@@ -32,15 +32,15 @@ define('share', function() {
 		});
 
 		addHandler('.twitter-share', function () {
-			return openShare('https://twitter.com/intent/tweet?text=' + name + '&url=', getPostHash($(this)), 550, 420);
+			return openShare('https://twitter.com/intent/tweet?text=' + name + '&url=', getPostUrl($(this)), 550, 420);
 		});
 
 		addHandler('.facebook-share', function () {
-			return openShare('https://www.facebook.com/sharer/sharer.php?u=', getPostHash($(this)), 626, 436);
+			return openShare('https://www.facebook.com/sharer/sharer.php?u=', getPostUrl($(this)), 626, 436);
 		});
 
 		addHandler('.google-share', function () {
-			return openShare('https://plus.google.com/share?url=', getPostHash($(this)), 500, 570);
+			return openShare('https://plus.google.com/share?url=', getPostUrl($(this)), 500, 570);
 		});
 	};
 
@@ -49,11 +49,9 @@ define('share', function() {
 	}
 
 	function getPostHash(clickedElement) {
-		var pid = clickedElement.parents('.post-row').attr('data-pid');
-		if (pid) {
-			return '#' + pid;
-		}
-		return '';
+		var parts = window.location.pathname.split('/');
+		var postIndex = parseInt(clickedElement.parents('.post-row').attr('data-index'), 10);
+		return '/topic/' + parts[2] + (parts[3] ? '/' + parts[3] : '') + (postIndex ? '/' + (postIndex + 1) : '');
 	}
 
 	return module;
diff --git a/src/controllers/topics.js b/src/controllers/topics.js
index f9a87d2d79..4c0cba514b 100644
--- a/src/controllers/topics.js
+++ b/src/controllers/topics.js
@@ -20,7 +20,7 @@ topicsController.get = function(req, res, next) {
 		userPrivileges;
 
 	async.waterfall([
-		function(next) {
+		function (next) {
 			privileges.topics.get(tid, uid, function(err, privileges) {
 				if (err) {
 					return next(err);
@@ -40,8 +40,16 @@ topicsController.get = function(req, res, next) {
 					return next(err);
 				}
 
-				var start = (page - 1) * settings.postsPerPage,
-					end = start + settings.postsPerPage - 1;
+				var postIndex = 0;
+				if (!settings.usePagination) {
+					postIndex = Math.max((req.params.post_index || 1) - (settings.postsPerPage), 0);
+				} else if (!req.query.page) {
+					var index = Math.max(parseInt((req.params.post_index || 0), 10), 0);
+					page = Math.ceil((index + 1) / settings.postsPerPage);
+				}
+
+				var start = (page - 1) * settings.postsPerPage + postIndex,
+					end = start + settings.postsPerPage - 1 + postIndex;
 
 				topics.getTopicWithPosts(tid, uid, start, end, function (err, topicData) {
 					if (topicData) {
diff --git a/src/posts.js b/src/posts.js
index ffd1a3b61a..69f555a05c 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -274,6 +274,9 @@ var db = require('./database'),
 					}
 
 					postTools.parse(post.content, next);
+				},
+				index: function(next) {
+					Posts.getPidIndex(post.pid, next);
 				}
 			}, function(err, results) {
 				if (err) {
@@ -283,6 +286,7 @@ var db = require('./database'),
 				post.user = results.user;
 				post.topic = results.topicCategory.topic;
 				post.category = results.topicCategory.category;
+				post.index = parseInt(results.index, 10) + 1;
 
 				if (stripTags) {
 					var s = S(results.content);
diff --git a/src/routes/index.js b/src/routes/index.js
index e719852ab2..a1fd3d1965 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -51,6 +51,9 @@ function staticRoutes(app, middleware, controllers) {
 function topicRoutes(app, middleware, controllers) {
 	app.get('/api/topic/teaser/:topic_id', controllers.topics.teaser);
 
+	app.get('/topic/:topic_id/:slug/:post_index?', middleware.buildHeader, middleware.addSlug, controllers.topics.get);
+	app.get('/api/topic/:topic_id/:slug/:post_index?', controllers.topics.get);
+
 	app.get('/topic/:topic_id/:slug?', middleware.buildHeader, middleware.addSlug, controllers.topics.get);
 	app.get('/api/topic/:topic_id/:slug?', controllers.topics.get);
 }
diff --git a/src/topics.js b/src/topics.js
index 76b01ae036..05b57f5d33 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -312,29 +312,40 @@ var async = require('async'),
 
 	Topics.getTeaser = function(tid, callback) {
 		Topics.getLatestUndeletedPid(tid, function(err, pid) {
-			if (err) {
+			if (err || !pid) {
 				return callback(err);
 			}
 
-			if (!pid) {
-				return callback(null, null);
-			}
+			async.parallel({
+				postData: function(next) {
+					posts.getPostFields(pid, ['pid', 'uid', 'timestamp'], function(err, postData) {
+						if (err) {
+							return next(err);
+						} else if(!postData || !utils.isNumber(postData.uid)) {
+							return next(new Error('[[error:no-teaser]]'));
+						}
 
-			posts.getPostFields(pid, ['pid', 'uid', 'timestamp'], function(err, postData) {
+						user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
+							if (err) {
+								return next(err);
+							}
+							postData.user = userData;
+							next(null, postData);
+						});
+					});
+				},
+				postIndex: function(next) {
+					posts.getPidIndex(pid, next);
+				}
+			}, function(err, results) {
 				if (err) {
 					return callback(err);
-				} else if(!postData || !utils.isNumber(postData.uid)) {
-					return callback(new Error('[[error:no-teaser]]'));
 				}
 
-				user.getUserFields(postData.uid, ['username', 'userslug', 'picture'], function(err, userData) {
-					if (err) {
-						return callback(err);
-					}
-					postData.timestamp = utils.toISOString(postData.timestamp);
-					postData.user = userData;
-					callback(null, postData);
-				});
+				results.postData.timestamp = utils.toISOString(results.postData.timestamp);
+				results.postData.index = parseInt(results.postIndex, 10) + 1;
+
+				callback(null, results.postData);
 			});
 		});
 	};
diff --git a/src/topics/create.js b/src/topics/create.js
index 210545d690..7bf23b4bed 100644
--- a/src/topics/create.js
+++ b/src/topics/create.js
@@ -24,9 +24,15 @@ module.exports = function(Topics) {
 				return callback(err);
 			}
 
-			var slug = tid + '/' + utils.slugify(title),
+			var slug = utils.slugify(title),
 				timestamp = Date.now();
 
+			if (!slug.length) {
+				return callback(new Error('[[error:invalid-title]]'));
+			}
+
+			slug = tid + '/' + slug;
+
 			var topicData = {
 				'tid': tid,
 				'uid': uid,
diff --git a/src/topics/posts.js b/src/topics/posts.js
index d836fc7536..0f81a1f8e6 100644
--- a/src/topics/posts.js
+++ b/src/topics/posts.js
@@ -29,7 +29,7 @@ module.exports = function(Topics) {
 			if (Array.isArray(postData) && !postData.length) {
 				return callback(null, []);
 			}
-
+			start = parseInt(start, 10);
 			for(var i=0; i<postData.length; ++i) {
 				postData[i].index = start + i;
 			}