diff --git a/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx b/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
index 9ea7f190fb40eebf176993139c6c57ded12bb6ee..5591b45cfbb64e61cdf0dd6b9fd6e35508f9392a 100644
--- a/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
+++ b/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
@@ -1,11 +1,16 @@
 import Avatar from '../../../components/avatar';
 import DisplayName from '../../../components/display_name';
+import ImmutablePropTypes from 'react-immutable-proptypes';
 
 const AutosuggestAccount = ({ account }) => (
-  <div style={{ overflow: 'hidden' }}>
+  <div style={{ overflow: 'hidden' }} className='autosuggest-account'>
     <div style={{ float: 'left', marginRight: '5px' }}><Avatar src={account.get('avatar')} size={18} /></div>
     <DisplayName account={account} />
   </div>
 );
 
+AutosuggestAccount.propTypes = {
+  account: ImmutablePropTypes.map.isRequired
+};
+
 export default AutosuggestAccount;
diff --git a/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx b/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..086488649dc59d84c72bf36fb1c0819157dabb01
--- /dev/null
+++ b/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx
@@ -0,0 +1,15 @@
+import { FormattedMessage } from 'react-intl';
+import DisplayName from '../../../components/display_name';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+
+const AutosuggestStatus = ({ status }) => (
+  <div style={{ overflow: 'hidden' }} className='autosuggest-status'>
+    <FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} />
+  </div>
+);
+
+AutosuggestStatus.propTypes = {
+  status: ImmutablePropTypes.map.isRequired
+};
+
+export default AutosuggestStatus;
diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx
index c1f23939dbc68cbcbd91bc3dc6ad976e48163576..a0e8f82fbc7732800b6df185e1d3f4d69757c1b3 100644
--- a/app/assets/javascripts/components/features/compose/components/search.jsx
+++ b/app/assets/javascripts/components/features/compose/components/search.jsx
@@ -2,6 +2,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Autosuggest from 'react-autosuggest';
 import AutosuggestAccountContainer from '../containers/autosuggest_account_container';
+import AutosuggestStatusContainer from '../containers/autosuggest_status_container';
 import { debounce } from 'react-decoration';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 
@@ -14,8 +15,10 @@ const getSuggestionValue = suggestion => suggestion.value;
 const renderSuggestion = suggestion => {
   if (suggestion.type === 'account') {
     return <AutosuggestAccountContainer id={suggestion.id} />;
+  } else if (suggestion.type === 'hashtag') {
+    return <span>#{suggestion.id}</span>;
   } else {
-    return <span>#{suggestion.id}</span>
+    return <AutosuggestStatusContainer id={suggestion.id} />;
   }
 };
 
@@ -78,8 +81,10 @@ const Search = React.createClass({
   onSuggestionSelected (_, { suggestion }) {
     if (suggestion.type === 'account') {
       this.context.router.push(`/accounts/${suggestion.id}`);
-    } else {
+    } else if(suggestion.type === 'hashtag') {
       this.context.router.push(`/timelines/tag/${suggestion.id}`);
+    } else {
+      this.context.router.push(`/statuses/${suggestion.id}`);
     }
   },
 
diff --git a/app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx b/app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..ef46eb09caa9d6cf29164ab8e4d9fc0ce8a192de
--- /dev/null
+++ b/app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import AutosuggestStatus from '../components/autosuggest_status';
+import { makeGetStatus } from '../../../selectors';
+
+const makeMapStateToProps = () => {
+  const getStatus = makeGetStatus();
+
+  const mapStateToProps = (state, { id }) => ({
+    status: getStatus(state, id)
+  });
+
+  return mapStateToProps;
+};
+
+export default connect(makeMapStateToProps)(AutosuggestStatus);
diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx
index f3938cee1bee79275d591bade062e88d582ce832..6ce41670d60a748cb6b7fbce752302aebe516d7b 100644
--- a/app/assets/javascripts/components/reducers/accounts.jsx
+++ b/app/assets/javascripts/components/reducers/accounts.jsx
@@ -90,7 +90,6 @@ export default function accounts(state = initialState, action) {
   case REBLOGS_FETCH_SUCCESS:
   case FAVOURITES_FETCH_SUCCESS:
   case COMPOSE_SUGGESTIONS_READY:
-  case SEARCH_SUGGESTIONS_READY:
   case FOLLOW_REQUESTS_FETCH_SUCCESS:
   case FOLLOW_REQUESTS_EXPAND_SUCCESS:
   case BLOCKS_FETCH_SUCCESS:
@@ -98,6 +97,7 @@ export default function accounts(state = initialState, action) {
     return normalizeAccounts(state, action.accounts);
   case NOTIFICATIONS_REFRESH_SUCCESS:
   case NOTIFICATIONS_EXPAND_SUCCESS:
+  case SEARCH_SUGGESTIONS_READY:
     return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
   case TIMELINE_REFRESH_SUCCESS:
   case TIMELINE_EXPAND_SUCCESS:
diff --git a/app/assets/javascripts/components/reducers/search.jsx b/app/assets/javascripts/components/reducers/search.jsx
index 1767be2c64388e0b1787685cdfb0bce64df83414..e95f9ed79f611d272c8060e80c7695f03ddc2bed 100644
--- a/app/assets/javascripts/components/reducers/search.jsx
+++ b/app/assets/javascripts/components/reducers/search.jsx
@@ -32,7 +32,7 @@ const normalizeSuggestions = (state, value, accounts, hashtags, statuses) => {
       value: `#${item}`
     }));
 
-    if (value.indexOf('@') === -1 && value.indexOf(' ') === -1 && hashtags.indexOf(value) === -1) {
+    if (value.indexOf('@') === -1 && value.indexOf(' ') === -1 && !value.startsWith('http://') && !value.startsWith('https://') && hashtags.indexOf(value) === -1) {
       hashtagItems.unshift({
         type: 'hashtag',
         id: value,
@@ -40,9 +40,22 @@ const normalizeSuggestions = (state, value, accounts, hashtags, statuses) => {
       });
     }
 
+    if (hashtagItems.length > 0) {
+      newSuggestions.push({
+        title: 'hashtag',
+        items: hashtagItems
+      });
+    }
+  }
+
+  if (statuses.length > 0) {
     newSuggestions.push({
-      title: 'hashtag',
-      items: hashtagItems
+      title: 'status',
+      items: statuses.map(item => ({
+        type: 'status',
+        id: item.id,
+        value: item.id
+      }))
     });
   }
 
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index 4b1e86aca4724e9dcfc7461d88c9a6f810617a3c..057c61f911f9232bc9500631970ee8b67d1a2635 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -1421,3 +1421,13 @@ button.active i.fa-retweet {
     }
   }
 }
+
+.autosuggest-status {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+
+  strong {
+    font-weight: 500;
+  }
+}
diff --git a/app/models/account.rb b/app/models/account.rb
index c0cd2ff64c5f01cd26b219806378c2bf4ef6e84f..6968607a2af6fbcb0bc07f3a1e0e253917c5625b 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -222,8 +222,9 @@ SQL
     end
 
     def search_for(terms, limit = 10)
+      terms      = Arel.sql(connection.quote(terms.gsub(/['?\\:]/, ' ')))
       textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
-      query      = 'to_tsquery(\'simple\', \'\'\' \' || ? || \' \'\'\' || \':*\')'
+      query      = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
 
       sql = <<SQL
         SELECT
@@ -235,12 +236,13 @@ SQL
         LIMIT ?
 SQL
 
-      Account.find_by_sql([sql, terms, terms, limit])
+      Account.find_by_sql([sql, limit])
     end
 
     def advanced_search_for(terms, account, limit = 10)
+      terms      = Arel.sql(connection.quote(terms.gsub(/['?\\:]/, ' ')))
       textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
-      query      = 'to_tsquery(\'simple\', \'\'\' \' || ? || \' \'\'\' || \':*\')'
+      query      = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
 
       sql = <<SQL
         SELECT
@@ -254,7 +256,7 @@ SQL
         LIMIT ?
 SQL
 
-      Account.find_by_sql([sql, terms, account.id, account.id, terms, limit])
+      Account.find_by_sql([sql, account.id, account.id, limit])
     end
 
     def following_map(target_account_ids, account_id)
diff --git a/app/models/tag.rb b/app/models/tag.rb
index e2ad8e4db588e2a2d846f70bd7bea300b57cd1a7..15625ca435ea005a952feb4bb1035b1dc1b6bbc1 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -13,8 +13,9 @@ class Tag < ApplicationRecord
 
   class << self
     def search_for(terms, limit = 5)
+      terms      = Arel.sql(connection.quote(terms.gsub(/['?\\:]/, ' ')))
       textsearch = 'to_tsvector(\'simple\', tags.name)'
-      query      = 'to_tsquery(\'simple\', \'\'\' \' || ? || \' \'\'\' || \':*\')'
+      query      = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
 
       sql = <<SQL
         SELECT
@@ -26,7 +27,7 @@ class Tag < ApplicationRecord
         LIMIT ?
 SQL
 
-      Tag.find_by_sql([sql, terms, terms, limit])
+      Tag.find_by_sql([sql, limit])
     end
   end
 end
diff --git a/app/services/fetch_remote_account_service.rb b/app/services/fetch_remote_account_service.rb
index baefa3a86144f32d5e822fdcee1729c9f64043e1..6a6a696d6ce0fb8fa95ee89abbb3429c44faccec 100644
--- a/app/services/fetch_remote_account_service.rb
+++ b/app/services/fetch_remote_account_service.rb
@@ -1,8 +1,13 @@
 # frozen_string_literal: true
 
 class FetchRemoteAccountService < BaseService
-  def call(url)
-    atom_url, body = FetchAtomService.new.call(url)
+  def call(url, prefetched_body = nil)
+    if prefetched_body.nil?
+      atom_url, body = FetchAtomService.new.call(url)
+    else
+      atom_url = url
+      body     = prefetched_body
+    end
 
     return nil if atom_url.nil?
     process_atom(atom_url, body)
diff --git a/app/services/fetch_remote_resource_service.rb b/app/services/fetch_remote_resource_service.rb
index 80aa74365a3b4b83c99159c2e15d02e075014ff5..2185ceb202deab2761de40700ffc3e4e29d95509 100644
--- a/app/services/fetch_remote_resource_service.rb
+++ b/app/services/fetch_remote_resource_service.rb
@@ -10,9 +10,9 @@ class FetchRemoteResourceService < BaseService
     xml.encoding = 'utf-8'
 
     if xml.root.name == 'feed'
-      FetchRemoteAccountService.new.call(atom_url)
+      FetchRemoteAccountService.new.call(atom_url, body)
     elsif xml.root.name == 'entry'
-      FetchRemoteStatusService.new.call(atom_url)
+      FetchRemoteStatusService.new.call(atom_url, body)
     end
   end
 end
diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb
index 7063231e43e3d160c918c1258a160ed368a8f68d..e2d18572312c07806d4b915392433f53bcd4d958 100644
--- a/app/services/fetch_remote_status_service.rb
+++ b/app/services/fetch_remote_status_service.rb
@@ -1,8 +1,13 @@
 # frozen_string_literal: true
 
 class FetchRemoteStatusService < BaseService
-  def call(url)
-    atom_url, body = FetchAtomService.new.call(url)
+  def call(url, prefetched_body = nil)
+    if prefetched_body.nil?
+      atom_url, body = FetchAtomService.new.call(url)
+    else
+      atom_url = url
+      body     = prefetched_body
+    end
 
     return nil if atom_url.nil?
     process_atom(atom_url, body)
diff --git a/db/migrate/20170322162804_add_search_index_to_tags.rb b/db/migrate/20170322162804_add_search_index_to_tags.rb
new file mode 100644
index 0000000000000000000000000000000000000000..415dff9a07679911f4bc331f03d10d2e27ed3dbc
--- /dev/null
+++ b/db/migrate/20170322162804_add_search_index_to_tags.rb
@@ -0,0 +1,9 @@
+class AddSearchIndexToTags < ActiveRecord::Migration[5.0]
+  def up
+    execute 'CREATE INDEX hashtag_search_index ON tags USING gin(to_tsvector(\'simple\', tags.name));'
+  end
+
+  def down
+    remove_index :tags, name: :hashtag_search_index
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 44b52c220b495d653d32d257cc896b1bebd60041..2457b523dfbc812f9de18248f35a36be691733fa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20170322143850) do
+ActiveRecord::Schema.define(version: 20170322162804) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -259,6 +259,7 @@ ActiveRecord::Schema.define(version: 20170322143850) do
     t.string   "name",       default: "", null: false
     t.datetime "created_at",              null: false
     t.datetime "updated_at",              null: false
+    t.index "to_tsvector('simple'::regconfig, (name)::text)", name: "hashtag_search_index", using: :gin
     t.index ["name"], name: "index_tags_on_name", unique: true, using: :btree
   end