diff --git a/app/javascript/mastodon/features/compose/components/upload_button.js b/app/javascript/mastodon/features/compose/components/upload_button.js
index d550019f41841ba3039fab9bd16fbcd76d2f5487..08da1306d92f26b04db8d1aa01e89c2b608b2661 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.js
+++ b/app/javascript/mastodon/features/compose/components/upload_button.js
@@ -10,7 +10,7 @@ const messages = defineMessages({
   upload: { id: 'upload_button.label', defaultMessage: 'Add media ({formats})' },
 });
 
-const SUPPORTED_FORMATS = 'JPEG, PNG, GIF, WebM, MP4, MOV, OGG, WAV, MP3, FLAC';
+const SUPPORTED_FORMATS = 'JPEG, PNG, GIF, WEBP, HEIF, HEIC, WebM, MP4, MOV, OGG, WAV, MP3, FLAC';
 
 const makeMapStateToProps = () => {
   const mapStateToProps = state => ({
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
index 7196dc96b1e9b26ab3db09072188f294529e4249..b0f2fd565971d2e33949fe12fa63775e5d87e2a0 100644
--- a/app/javascript/mastodon/utils/resize_image.js
+++ b/app/javascript/mastodon/utils/resize_image.js
@@ -95,8 +95,10 @@ const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) =
     .catch(reject);
 });
 
+const WITHOUT_RESIZING_FORMATS = ['image/gif', 'image/heic', 'image/heif'];
+
 export default inputFile => new Promise((resolve, reject) => {
-  if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
+  if (!inputFile.type.match(/image.*/) || WITHOUT_RESIZING_FORMATS.indexOf(inputFile.type) >= 0) {
     resolve(inputFile);
     return;
   }
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 1fd0adfd02ba74976da02b9ddff224f8ac3aace8..6468d104d253c2837d8fe02c0dad140c53058f57 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -28,11 +28,12 @@ class MediaAttachment < ApplicationRecord
 
   MAX_DESCRIPTION_LENGTH = 1_500
 
-  IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif).freeze
+  IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heif .heic).freeze
   VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze
   AUDIO_FILE_EXTENSIONS = %w(.ogg .oga .mp3 .wav .flac .opus .aac .m4a .3gp .wma).freeze
 
-  IMAGE_MIME_TYPES             = %w(image/jpeg image/png image/gif).freeze
+  IMAGE_MIME_TYPES             = %w(image/jpeg image/png image/gif image/webp image/heif image/heic).freeze
+  IMAGE_CONVERTIBLE_MIME_TYPES = %w(image/webp image/heif image/heic).freeze
   VIDEO_MIME_TYPES             = %w(video/webm video/mp4 video/quicktime video/ogg).freeze
   VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime).freeze
   AUDIO_MIME_TYPES             = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/ogg audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze
@@ -240,6 +241,8 @@ class MediaAttachment < ApplicationRecord
         [:video_transcoder, :blurhash_transcoder, :type_corrector]
       elsif AUDIO_MIME_TYPES.include?(f.file_content_type)
         [:transcoder, :type_corrector]
+      elsif IMAGE_CONVERTIBLE_MIME_TYPES.include?(f.file_content_type)
+        [:img_converter, :lazy_thumbnail, :blurhash_transcoder, :type_corrector]
       else
         [:lazy_thumbnail, :blurhash_transcoder, :type_corrector]
       end
diff --git a/lib/paperclip/img_converter.rb b/lib/paperclip/img_converter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3f4e949359dd7b2565551713dfd1641527c699b1
--- /dev/null
+++ b/lib/paperclip/img_converter.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Paperclip
+  class ImgConverter < Paperclip::Processor
+    def initialize(file, options = {}, attachment = nil)
+      super
+
+      @current_format = File.extname(@file.path)
+      @basename       = File.basename(@file.path, @current_format)
+    end
+
+    def make
+      dst_format, dst_content_type = opaque? ? ['jpg', 'image/jpeg'] : ['png', 'image/png']
+      dst_name = "#{@basename}.#{dst_format}"
+
+      attachment.instance.file_file_name = dst_name
+      attachment.instance.file_content_type = dst_content_type
+
+      options[:format] = dst_format
+      options[:content_type] = dst_content_type
+
+      dst = Paperclip::TempfileFactory.new.generate(dst_name)
+      convert(':src :dst', src: File.expand_path(@file.path), dst: File.expand_path(dst.path))
+
+      dst
+    end
+
+    private
+
+    def opaque?
+      identify('-format "%[opaque]" :src', src: File.expand_path(@file.path)).strip.downcase == 'true'
+    end
+  end
+end