diff --git a/internal/federation/dereferencing/account.go b/internal/federation/dereferencing/account.go
index bd97b91ed..33a71ceb9 100644
--- a/internal/federation/dereferencing/account.go
+++ b/internal/federation/dereferencing/account.go
@@ -20,7 +20,6 @@ package dereferencing
 import (
 	"context"
 	"errors"
-	"io"
 	"net/url"
 	"time"
 
@@ -752,128 +751,146 @@ func (d *Dereferencer) enrichAccount(
 	return latestAcc, apubAcc, nil
 }
 
-func (d *Dereferencer) fetchRemoteAccountAvatar(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error {
+func (d *Dereferencer) fetchRemoteAccountAvatar(
+	ctx context.Context,
+	tsport transport.Transport,
+	existingAcc *gtsmodel.Account,
+	latestAcc *gtsmodel.Account,
+) error {
 	if latestAcc.AvatarRemoteURL == "" {
 		// No avatar set on newest model, leave
 		// latest avatar attachment ID empty.
 		return nil
 	}
 
-	// By default we keep the previous media attachment ID. This will only
-	// be changed if and when we have the new media loaded into storage.
-	latestAcc.AvatarMediaAttachmentID = existing.AvatarMediaAttachmentID
+	// Check for an existing stored media attachment
+	// specifically with unchanged remote URL we can use.
+	if existingAcc.AvatarMediaAttachmentID != "" &&
+		existingAcc.AvatarRemoteURL == latestAcc.AvatarRemoteURL {
 
-	// If we had a media attachment ID already, and the URL
-	// of the attachment hasn't changed from existing -> latest,
-	// then we may be able to just keep our existing attachment
-	// without having to make any remote calls.
-	if latestAcc.AvatarMediaAttachmentID != "" &&
-		existing.AvatarRemoteURL == latestAcc.AvatarRemoteURL {
-
-		// Ensure we have media attachment with the known ID.
-		media, err := d.state.DB.GetAttachmentByID(ctx, existing.AvatarMediaAttachmentID)
+		// Fetch the existing avatar media attachment with ID.
+		existing, err := d.state.DB.GetAttachmentByID(ctx,
+			existingAcc.AvatarMediaAttachmentID,
+		)
 		if err != nil && !errors.Is(err, db.ErrNoEntries) {
-			return gtserror.Newf("error getting attachment %s: %w", existing.AvatarMediaAttachmentID, err)
+			return gtserror.Newf("error getting attachment %s: %w", existingAcc.AvatarMediaAttachmentID, err)
 		}
 
-		// Ensure attachment has correct properties.
-		if media != nil && media.RemoteURL == latestAcc.AvatarRemoteURL {
-			// We already have the most up-to-date
-			// media attachment, keep using it.
+		if existing != nil {
+			// Ensuring existing attachment is up-to-date
+			// and any recaching is performed if required.
+			existing, err := d.updateAttachment(ctx,
+				tsport,
+				existing,
+				nil,
+			)
+
+			if err != nil {
+				log.Errorf(ctx, "error updating existing attachment: %v", err)
+
+				// specifically do NOT return nil here,
+				// we already have a model, we don't
+				// want to drop it from the account, just
+				// log that an update for it failed.
+			}
+
+			// Set the avatar attachment on account model.
+			latestAcc.AvatarMediaAttachment = existing
+			latestAcc.AvatarMediaAttachmentID = existing.ID
+
 			return nil
 		}
 	}
 
-	// If we reach here, we know we need to fetch the most
-	// up-to-date version of the attachment from remote.
-
-	// Parse and validate the newly provided media URL.
-	avatarURI, err := url.Parse(latestAcc.AvatarRemoteURL)
+	// Fetch newly changed avatar from remote.
+	attachment, err := d.loadAttachment(ctx,
+		tsport,
+		latestAcc.ID,
+		latestAcc.AvatarRemoteURL,
+		&media.AdditionalMediaInfo{
+			Avatar:    util.Ptr(true),
+			RemoteURL: &latestAcc.AvatarRemoteURL,
+		},
+	)
 	if err != nil {
-		return gtserror.Newf("error parsing url %s: %w", latestAcc.AvatarRemoteURL, err)
-	}
-
-	// Set the media data function to dereference avatar from URI.
-	data := func(ctx context.Context) (io.ReadCloser, int64, error) {
-		return tsport.DereferenceMedia(ctx, avatarURI)
-	}
-
-	// Create new media processing request from the media manager instance.
-	processing := d.mediaManager.PreProcessMedia(data, latestAcc.ID, &media.AdditionalMediaInfo{
-		Avatar:    func() *bool { v := true; return &v }(),
-		RemoteURL: &latestAcc.AvatarRemoteURL,
-	})
-
-	// Start media attachment loading (blocking call).
-	if _, err := processing.LoadAttachment(ctx); err != nil {
 		return gtserror.Newf("error loading attachment %s: %w", latestAcc.AvatarRemoteURL, err)
 	}
 
-	// Set the newly loaded avatar media attachment ID.
-	latestAcc.AvatarMediaAttachmentID = processing.AttachmentID()
+	// Set the avatar attachment on account model.
+	latestAcc.AvatarMediaAttachment = attachment
+	latestAcc.AvatarMediaAttachmentID = attachment.ID
 
 	return nil
 }
 
-func (d *Dereferencer) fetchRemoteAccountHeader(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error {
+func (d *Dereferencer) fetchRemoteAccountHeader(
+	ctx context.Context,
+	tsport transport.Transport,
+	existingAcc *gtsmodel.Account,
+	latestAcc *gtsmodel.Account,
+) error {
 	if latestAcc.HeaderRemoteURL == "" {
 		// No header set on newest model, leave
 		// latest header attachment ID empty.
 		return nil
 	}
 
-	// By default we keep the previous media attachment ID. This will only
-	// be changed if and when we have the new media loaded into storage.
-	latestAcc.HeaderMediaAttachmentID = existing.HeaderMediaAttachmentID
+	// Check for an existing stored media attachment
+	// specifically with unchanged remote URL we can use.
+	if existingAcc.HeaderMediaAttachmentID != "" &&
+		existingAcc.HeaderRemoteURL == latestAcc.HeaderRemoteURL {
 
-	// If we had a media attachment ID already, and the URL
-	// of the attachment hasn't changed from existing -> latest,
-	// then we may be able to just keep our existing attachment
-	// without having to make any remote calls.
-	if latestAcc.HeaderMediaAttachmentID != "" &&
-		existing.HeaderRemoteURL == latestAcc.HeaderRemoteURL {
-
-		// Ensure we have media attachment with the known ID.
-		media, err := d.state.DB.GetAttachmentByID(ctx, existing.HeaderMediaAttachmentID)
+		// Fetch the existing header media attachment with ID.
+		existing, err := d.state.DB.GetAttachmentByID(ctx,
+			existingAcc.HeaderMediaAttachmentID,
+		)
 		if err != nil && !errors.Is(err, db.ErrNoEntries) {
-			return gtserror.Newf("error getting attachment %s: %w", existing.HeaderMediaAttachmentID, err)
+			return gtserror.Newf("error getting attachment %s: %w", existingAcc.HeaderMediaAttachmentID, err)
 		}
 
-		// Ensure attachment has correct properties.
-		if media != nil && media.RemoteURL == latestAcc.HeaderRemoteURL {
-			// We already have the most up-to-date
-			// media attachment, keep using it.
+		if existing != nil {
+			// Ensuring existing attachment is up-to-date
+			// and any recaching is performed if required.
+			existing, err := d.updateAttachment(ctx,
+				tsport,
+				existing,
+				nil,
+			)
+
+			if err != nil {
+				log.Errorf(ctx, "error updating existing attachment: %v", err)
+
+				// specifically do NOT return nil here,
+				// we already have a model, we don't
+				// want to drop it from the account, just
+				// log that an update for it failed.
+			}
+
+			// Set the header attachment on account model.
+			latestAcc.HeaderMediaAttachment = existing
+			latestAcc.HeaderMediaAttachmentID = existing.ID
+
 			return nil
 		}
 	}
 
-	// If we reach here, we know we need to fetch the most
-	// up-to-date version of the attachment from remote.
-
-	// Parse and validate the newly provided media URL.
-	headerURI, err := url.Parse(latestAcc.HeaderRemoteURL)
+	// Fetch newly changed header from remote.
+	attachment, err := d.loadAttachment(ctx,
+		tsport,
+		latestAcc.ID,
+		latestAcc.HeaderRemoteURL,
+		&media.AdditionalMediaInfo{
+			Header:    util.Ptr(true),
+			RemoteURL: &latestAcc.HeaderRemoteURL,
+		},
+	)
 	if err != nil {
-		return gtserror.Newf("error parsing url %s: %w", latestAcc.HeaderRemoteURL, err)
-	}
-
-	// Set the media data function to dereference avatar from URI.
-	data := func(ctx context.Context) (io.ReadCloser, int64, error) {
-		return tsport.DereferenceMedia(ctx, headerURI)
-	}
-
-	// Create new media processing request from the media manager instance.
-	processing := d.mediaManager.PreProcessMedia(data, latestAcc.ID, &media.AdditionalMediaInfo{
-		Header:    func() *bool { v := true; return &v }(),
-		RemoteURL: &latestAcc.HeaderRemoteURL,
-	})
-
-	// Start media attachment loading (blocking call).
-	if _, err := processing.LoadAttachment(ctx); err != nil {
 		return gtserror.Newf("error loading attachment %s: %w", latestAcc.HeaderRemoteURL, err)
 	}
 
-	// Set the newly loaded avatar media attachment ID.
-	latestAcc.HeaderMediaAttachmentID = processing.AttachmentID()
+	// Set the header attachment on account model.
+	latestAcc.HeaderMediaAttachment = attachment
+	latestAcc.HeaderMediaAttachmentID = attachment.ID
 
 	return nil
 }
diff --git a/internal/federation/dereferencing/status.go b/internal/federation/dereferencing/status.go
index 69627adc2..add12c31f 100644
--- a/internal/federation/dereferencing/status.go
+++ b/internal/federation/dereferencing/status.go
@@ -20,7 +20,6 @@ package dereferencing
 import (
 	"context"
 	"errors"
-	"io"
 	"net/url"
 	"slices"
 	"time"
@@ -1000,45 +999,45 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp
 
 		// Look for existing media attachment with remote URL first.
 		existing, ok := existing.GetAttachmentByRemoteURL(attachment.RemoteURL)
-		if ok && existing.ID != "" && *existing.Cached {
+		if ok && existing.ID != "" {
+
+			// Ensure the existing media attachment is up-to-date and cached.
+			existing, err := d.updateAttachment(ctx, tsport, existing, attachment)
+			if err != nil {
+				log.Errorf(ctx, "error updating existing attachment: %v", err)
+
+				// specifically do NOT continue here,
+				// we already have a model, we don't
+				// want to drop it from the status, just
+				// log that an update for it failed.
+			}
+
+			// Set the existing attachment.
 			status.Attachments[i] = existing
 			status.AttachmentIDs[i] = existing.ID
 			continue
 		}
 
-		// Ensure a valid media attachment remote URL.
-		remoteURL, err := url.Parse(attachment.RemoteURL)
-		if err != nil {
-			log.Errorf(ctx, "invalid remote media url %q: %v", attachment.RemoteURL, err)
+		// Load this new media attachment.
+		attachment, err := d.loadAttachment(
+			ctx,
+			tsport,
+			status.AccountID,
+			attachment.RemoteURL,
+			&media.AdditionalMediaInfo{
+				StatusID:    &status.ID,
+				RemoteURL:   &attachment.RemoteURL,
+				Description: &attachment.Description,
+				Blurhash:    &attachment.Blurhash,
+			},
+		)
+		if err != nil && attachment == nil {
+			log.Errorf(ctx, "error loading attachment: %v", err)
 			continue
 		}
 
-		data := func(ctx context.Context) (io.ReadCloser, int64, error) {
-			return tsport.DereferenceMedia(ctx, remoteURL)
-		}
-
-		ai := &media.AdditionalMediaInfo{
-			StatusID:    &status.ID,
-			RemoteURL:   &attachment.RemoteURL,
-			Description: &attachment.Description,
-			Blurhash:    &attachment.Blurhash,
-		}
-
-		// Start pre-processing remote media at remote URL.
-		processing := d.mediaManager.PreProcessMedia(data, status.AccountID, ai)
-
-		// Force attachment loading *right now*.
-		attachment, err = processing.LoadAttachment(ctx)
 		if err != nil {
-			if attachment == nil {
-				// Totally failed to load;
-				// bail on this attachment.
-				log.Errorf(ctx, "error loading attachment: %v", err)
-				continue
-			}
-
-			// Partially loaded. Keep as
-			// placeholder and try again later.
+			// A non-fatal error occurred during loading.
 			log.Warnf(ctx, "partially loaded attachment: %v", err)
 		}
 
diff --git a/internal/federation/dereferencing/util.go b/internal/federation/dereferencing/util.go
index 38622f6c1..5cb7a0106 100644
--- a/internal/federation/dereferencing/util.go
+++ b/internal/federation/dereferencing/util.go
@@ -18,11 +18,122 @@
 package dereferencing
 
 import (
+	"context"
+	"io"
+	"net/url"
 	"slices"
 
+	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+	"github.com/superseriousbusiness/gotosocial/internal/media"
+	"github.com/superseriousbusiness/gotosocial/internal/transport"
+	"github.com/superseriousbusiness/gotosocial/internal/util"
 )
 
+// loadAttachment handles the case of a new media attachment
+// that requires loading. it stores and caches from given data.
+func (d *Dereferencer) loadAttachment(
+	ctx context.Context,
+	tsport transport.Transport,
+	accountID string, // media account owner
+	remoteURL string,
+	info *media.AdditionalMediaInfo,
+) (
+	*gtsmodel.MediaAttachment,
+	error,
+) {
+	// Parse str as valid URL object.
+	url, err := url.Parse(remoteURL)
+	if err != nil {
+		return nil, gtserror.Newf("invalid remote media url %q: %v", remoteURL, err)
+	}
+
+	// Start pre-processing remote media at remote URL.
+	processing := d.mediaManager.PreProcessMedia(
+		func(ctx context.Context) (io.ReadCloser, int64, error) {
+			return tsport.DereferenceMedia(ctx, url)
+		},
+		accountID,
+		info,
+	)
+
+	// Force attachment loading *right now*.
+	return processing.LoadAttachment(ctx)
+}
+
+// updateAttachment handles the case of an existing media attachment
+// that *may* have changes or need recaching. it checks for changed
+// fields, updating in the database if so, and recaches uncached media.
+func (d *Dereferencer) updateAttachment(
+	ctx context.Context,
+	tsport transport.Transport,
+	existing *gtsmodel.MediaAttachment, // existing attachment
+	media *gtsmodel.MediaAttachment, // (optional) changed media
+) (
+	*gtsmodel.MediaAttachment, // always set
+	error,
+) {
+	if media != nil {
+		// Possible changed media columns.
+		changed := make([]string, 0, 3)
+
+		// Check if attachment description has changed.
+		if existing.Description != media.Description {
+			changed = append(changed, "description")
+			existing.Description = media.Description
+		}
+
+		// Check if attachment blurhash has changed (i.e. content change).
+		if existing.Blurhash != media.Blurhash && media.Blurhash != "" {
+			changed = append(changed, "blurhash", "cached")
+			existing.Blurhash = media.Blurhash
+			existing.Cached = util.Ptr(false)
+		}
+
+		if len(changed) > 0 {
+			// Update the existing attachment model in the database.
+			err := d.state.DB.UpdateAttachment(ctx, existing, changed...)
+			if err != nil {
+				return media, gtserror.Newf("error updating media: %w", err)
+			}
+		}
+	}
+
+	// Check if cached.
+	if *existing.Cached {
+		return existing, nil
+	}
+
+	// Parse str as valid URL object.
+	url, err := url.Parse(existing.RemoteURL)
+	if err != nil {
+		return nil, gtserror.Newf("invalid remote media url %q: %v", media.RemoteURL, err)
+	}
+
+	// Start pre-processing remote media recaching from remote.
+	processing, err := d.mediaManager.PreProcessMediaRecache(
+		ctx,
+		func(ctx context.Context) (io.ReadCloser, int64, error) {
+			return tsport.DereferenceMedia(ctx, url)
+		},
+		existing.ID,
+	)
+	if err != nil {
+		return nil, gtserror.Newf("error processing recache: %w", err)
+	}
+
+	// Force load attachment recache *right now*.
+	recached, err := processing.LoadAttachment(ctx)
+
+	// Always return the error we
+	// receive, but ensure we return
+	// most up-to-date media file.
+	if recached != nil {
+		return recached, err
+	}
+	return existing, err
+}
+
 // pollChanged returns whether a poll has changed in way that
 // indicates that this should be an entirely new poll. i.e. if
 // the available options have changed, or the expiry has increased.