diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 6bbf6cf17949cf364e6f199a1290ac1a675aeb93..6c0947af52b6747155d8113f82b8e8a2f823c2a9 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -6,7 +6,7 @@ import PollForm from '../poll/poll_form.vue'
 import Attachment from '../attachment/attachment.vue'
 import fileTypeService from '../../services/file_type/file_type.service.js'
 import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
-import { reject, map, uniqBy } from 'lodash'
+import { reject, map, uniqBy, debounce } from 'lodash'
 import suggestor from '../emoji_input/suggestor.js'
 import { mapGetters } from 'vuex'
 import Checkbox from '../checkbox/checkbox.vue'
@@ -169,7 +169,7 @@ const PostStatusForm = {
     ...mapGetters(['mergedConfig'])
   },
   methods: {
-    postStatus (newStatus) {
+    async postStatus (newStatus) {
       if (this.posting) { return }
       if (this.submitDisabled) { return }
 
@@ -187,41 +187,43 @@ const PostStatusForm = {
       }
 
       this.posting = true
-      statusPoster.postStatus({
+
+      await this.setAllMediaDescriptions()
+
+      const data = await statusPoster.postStatus({
         status: newStatus.status,
         spoilerText: newStatus.spoilerText || null,
         visibility: newStatus.visibility,
         sensitive: newStatus.nsfw,
         media: newStatus.files,
-        mediaDescriptions: newStatus.mediaDescriptions || {},
         store: this.$store,
         inReplyToStatusId: this.replyTo,
         contentType: newStatus.contentType,
         poll
-      }).then((data) => {
-        if (!data.error) {
-          this.newStatus = {
-            status: '',
-            spoilerText: '',
-            files: [],
-            mediaDescriptions: {},
-            visibility: newStatus.visibility,
-            contentType: newStatus.contentType,
-            poll: {}
-          }
-          this.pollFormVisible = false
-          this.$refs.mediaUpload.clearFile()
-          this.clearPollForm()
-          this.$emit('posted')
-          let el = this.$el.querySelector('textarea')
-          el.style.height = 'auto'
-          el.style.height = undefined
-          this.error = null
-        } else {
-          this.error = data.error
-        }
-        this.posting = false
       })
+
+      if (!data.error) {
+        this.newStatus = {
+          status: '',
+          spoilerText: '',
+          files: [],
+          visibility: newStatus.visibility,
+          contentType: newStatus.contentType,
+          poll: {}
+        }
+        this.pollFormVisible = false
+        this.$refs.mediaUpload.clearFile()
+        this.clearPollForm()
+        this.$emit('posted')
+        let el = this.$el.querySelector('textarea')
+        el.style.height = 'auto'
+        el.style.height = undefined
+        this.error = null
+      } else {
+        this.error = data.error
+      }
+
+      this.posting = false
     },
     addMediaFile (fileInfo) {
       this.newStatus.files.push(fileInfo)
@@ -393,6 +395,23 @@ const PostStatusForm = {
     },
     dismissScopeNotice () {
       this.$store.dispatch('setOption', { name: 'hideScopeNotice', value: true })
+    },
+    debounceSendDescription: debounce(function (id, description) {
+      statusPoster.setMediaDescription({ store: this.$store, id, description })
+    }, 500),
+    updateMediaDescription (fileId, e) {
+      const description = e.target.value
+      this.newStatus.mediaDescriptions[fileId] = description
+      this.debounceSendDescription(fileId, description)
+    },
+    setMediaDescription (id) {
+      const description = this.newStatus.mediaDescriptions[id]
+      if (!description || description.trim() === '') return
+      return statusPoster.setMediaDescription({ store: this.$store, id, description })
+    },
+    setAllMediaDescriptions () {
+      const ids = this.newStatus.files.map(file => file.id)
+      return Promise.all(ids.map(id => this.setMediaDescription(id)))
     }
   }
 }
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 442b9297627145e80d044f66de92a807635d0640..ea7b5b80d429c34f6fb4b1c50ca38fc4f4f6ee5c 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -255,6 +255,7 @@
             v-model="newStatus.mediaDescriptions[file.id]"
             type="text"
             :placeholder="$t('post_status.media_description')"
+            @blur="setMediaDescription(file.id)"
             @keydown.enter.prevent=""
           >
         </div>
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index c4d7fb53f00e0a7d97a866f7b35fe7fcc09cd50f..067acbe9eda11e2064a718724486bf1f1d48a17a 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -645,8 +645,7 @@ const postStatus = ({
   poll,
   mediaIds = [],
   inReplyToStatusId,
-  contentType,
-  mediaDescriptions
+  contentType
 }) => {
   const form = new FormData()
   const pollOptions = poll.options || []
@@ -673,7 +672,6 @@ const postStatus = ({
       form.append('poll[options][]', option)
     })
   }
-  form.append('descriptions', JSON.stringify(mediaDescriptions))
   if (inReplyToStatusId) {
     form.append('in_reply_to_id', inReplyToStatusId)
   }
@@ -712,6 +710,17 @@ const uploadMedia = ({ formData, credentials }) => {
     .then((data) => parseAttachment(data))
 }
 
+const setMediaDescription = ({ id, description, credentials }) => {
+  return promisedRequest({
+    url: `${MASTODON_MEDIA_UPLOAD_URL}/${id}`,
+    method: 'PUT',
+    headers: authHeaders(credentials),
+    payload: {
+      description
+    }
+  }).then((data) => parseAttachment(data))
+}
+
 const importBlocks = ({ file, credentials }) => {
   const formData = new FormData()
   formData.append('list', file)
@@ -1181,6 +1190,7 @@ const apiService = {
   postStatus,
   deleteStatus,
   uploadMedia,
+  setMediaDescription,
   fetchMutes,
   muteUser,
   unmuteUser,
diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js
index 090ff6731bb0550328ed29d6561f6831dde10090..7f5f501caea68807acaab0b7c833ffe2037aac5c 100644
--- a/src/services/status_poster/status_poster.service.js
+++ b/src/services/status_poster/status_poster.service.js
@@ -1,7 +1,7 @@
 import { map } from 'lodash'
 import apiService from '../api/api.service.js'
 
-const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain', mediaDescriptions = {} }) => {
+const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
   const mediaIds = map(media, 'id')
 
   return apiService.postStatus({
@@ -13,8 +13,8 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, m
     mediaIds,
     inReplyToStatusId,
     contentType,
-    poll,
-    mediaDescriptions })
+    poll
+  })
     .then((data) => {
       if (!data.error) {
         store.dispatch('addNewStatuses', {
@@ -39,9 +39,16 @@ const uploadMedia = ({ store, formData }) => {
   return apiService.uploadMedia({ credentials, formData })
 }
 
+const setMediaDescription = ({ store, id, description }) => {
+  const credentials = store.state.users.currentUser.credentials
+
+  return apiService.setMediaDescription({ credentials, id, description })
+}
+
 const statusPosterService = {
   postStatus,
-  uploadMedia
+  uploadMedia,
+  setMediaDescription
 }
 
 export default statusPosterService