diff --git a/Changelog.md b/Changelog.md
index 4d32a3cfa963ffac57c8c1d09904033c695cbddf..a2bc1671d1ba94c05707badd3307dd55376bd167 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -32,6 +32,7 @@
 * Improve mobile usability [#4354](https://github.com/diaspora/diaspora/pull/4354)
 * Descending text is no longer cut off in orange welcome banner [#4377](https://github.com/diaspora/diaspora/issues/4377)
 * Adjust Facebook character limit to reality [#4380](https://github.com/diaspora/diaspora/issues/4380)
+* Restore truncated URLs when posting to Twitter [#4211](https://github.com/diaspora/diaspora/issues/4211)
 
 ## Features
 * Admin: add option to find users under 13 (COPPA) [#4252](https://github.com/diaspora/diaspora/pull/4252)
diff --git a/app/models/services/twitter.rb b/app/models/services/twitter.rb
index 0df15d9d81035d367c4408738e788fd5612605c5..3e264e4ca9f4fa594a07456438766d9f93421166 100644
--- a/app/models/services/twitter.rb
+++ b/app/models/services/twitter.rb
@@ -4,6 +4,8 @@ class Services::Twitter < Service
 
   MAX_CHARACTERS = 140
   SHORTENED_URL_LENGTH = 21
+  
+  LINK_PATTERN = %r{https?://\S+}
 
   def provider
     "twitter"
@@ -46,8 +48,10 @@ class Services::Twitter < Service
       :protocol => AppConfig.pod_uri.scheme, 
       :host => AppConfig.pod_uri.authority
     )
-    truncated = truncate(post_text, :length => (maxchars - (SHORTENED_URL_LENGTH+1) ))
-    post_text = "#{truncated} #{post_url}"
+    truncated_text = truncate post_text, length: maxchars - SHORTENED_URL_LENGTH + 1
+    truncated_text = restore_truncated_url truncated_text, post_text, maxchars
+
+    "#{truncated_text} #{post_url}"
   end
 
   def build_twitter_post(post, url, retry_count=0)
@@ -82,4 +86,18 @@ class Services::Twitter < Service
       oauth_token_secret: self.access_secret
     )
   end
+  
+  def restore_truncated_url truncated_text, post_text, maxchars
+      return truncated_text if truncated_text !~ /#{LINK_PATTERN}\Z/
+
+      url = post_text.match(LINK_PATTERN, truncated_text.rindex('http'))[0]
+      truncated_text = truncate(
+        post_text,
+        length: maxchars - SHORTENED_URL_LENGTH + 2,
+        separator: ' ',
+        omission: ''
+      )
+
+    "#{truncated_text} #{url} ..."
+  end
 end
diff --git a/spec/models/services/twitter_spec.rb b/spec/models/services/twitter_spec.rb
index d63f3ad694d8f985a8719fb27e4e4e2a71882cfb..0135da258ab5692732afce95125bb7003b4a3c58 100644
--- a/spec/models/services/twitter_spec.rb
+++ b/spec/models/services/twitter_spec.rb
@@ -71,6 +71,24 @@ describe Services::Twitter do
 
       answer.should_not match /\.\.\./
     end
+    
+    it "should not cut links when truncating a post" do
+      long_message = SecureRandom.hex(40) + " http://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + SecureRandom.hex(55)
+      long_post = stub(:text => long_message, :id => 1, :photos => [])
+      answer = @service.build_twitter_post(long_post, '')
+
+      answer.should match /\.\.\./
+      answer.should match /shortened\.html/
+    end
+    
+    it "should append the otherwise-cut link when truncating a post" do
+      long_message = "http://joindiaspora.com/a-very-long-decoy-url.html " + SecureRandom.hex(20) + " http://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + SecureRandom.hex(55) + " http://joindiaspora.com/a-very-long-decoy-url-part-2.html"
+      long_post = stub(:text => long_message, :id => 1, :photos => [])
+      answer = @service.build_twitter_post(long_post, '')
+      
+      answer.should match /\.\.\./
+      answer.should match /shortened\.html/
+    end
 
     it "should not truncate a long message with an https url" do
       long_message = " https://joindiaspora.com/a-very-long-url-name-that-will-be-shortened.html " + @long_message_end