diff --git a/keyserver/package.json b/keyserver/package.json
--- a/keyserver/package.json
+++ b/keyserver/package.json
@@ -35,6 +35,7 @@
     "concurrently": "^5.3.0",
     "flow-bin": "^0.182.0",
     "flow-typed": "^3.2.1",
+    "internal-ip": "4.3.0",
     "jest": "^26.6.3",
     "nodemon": "^2.0.4"
   },
diff --git a/keyserver/src/creators/upload-creator.js b/keyserver/src/creators/upload-creator.js
--- a/keyserver/src/creators/upload-creator.js
+++ b/keyserver/src/creators/upload-creator.js
@@ -2,7 +2,6 @@
 
 import crypto from 'crypto';
 
-import { shimUploadURI } from 'lib/media/media-utils.js';
 import type {
   MediaType,
   UploadMultimediaResult,
@@ -39,7 +38,7 @@
     return {
       uploadResult: {
         id,
-        uri: shimUploadURI(getUploadURL(id, secret), viewer.platformDetails),
+        uri: getUploadURL(id, secret),
         dimensions,
         mediaType,
         loop,
diff --git a/keyserver/src/fetchers/upload-fetchers.js b/keyserver/src/fetchers/upload-fetchers.js
--- a/keyserver/src/fetchers/upload-fetchers.js
+++ b/keyserver/src/fetchers/upload-fetchers.js
@@ -1,5 +1,6 @@
 // @flow
 
+import ip from 'internal-ip';
 import _keyBy from 'lodash/fp/keyBy.js';
 
 import type { Media } from 'lib/types/media-types.js';
@@ -11,6 +12,7 @@
   ThreadFetchMediaResult,
   ThreadFetchMediaRequest,
 } from 'lib/types/thread-types.js';
+import { isDev } from 'lib/utils/dev-utils.js';
 import { ServerError } from 'lib/utils/errors.js';
 
 import { dbQuery, SQL } from '../database/database.js';
@@ -86,7 +88,13 @@
 
 function getUploadURL(id: string, secret: string): string {
   const { baseDomain, basePath } = getAndAssertCommAppURLFacts();
-  return `${baseDomain}${basePath}upload/${id}/${secret}`;
+  const uploadPath = `${basePath}upload/${id}/${secret}`;
+  if (isDev) {
+    const ipV4 = ip.v4.sync() || 'localhost';
+    const port = parseInt(process.env.PORT, 10) || 3000;
+    return `http://${ipV4}:${port}${uploadPath}`;
+  }
+  return `${baseDomain}${uploadPath}`;
 }
 
 function mediaFromRow(row: Object): Media {
diff --git a/lib/media/media-utils.js b/lib/media/media-utils.js
--- a/lib/media/media-utils.js
+++ b/lib/media/media-utils.js
@@ -2,7 +2,6 @@
 
 import invariant from 'invariant';
 
-import type { PlatformDetails } from '../types/device-types.js';
 import type { Media } from '../types/media-types.js';
 import type {
   MultimediaMessageInfo,
@@ -11,15 +10,6 @@
 
 const maxDimensions = Object.freeze({ width: 1920, height: 1920 });
 
-const localhostRegex = /^http:\/\/localhost/;
-function shimUploadURI(uri: string, platformDetails: ?PlatformDetails): string {
-  if (!platformDetails || platformDetails.platform !== 'android') {
-    return uri;
-  }
-  // We do this for testing in the Android emulator
-  return uri.replace(localhostRegex, 'http://10.0.2.2');
-}
-
 function contentStringForMediaArray(media: $ReadOnlyArray<Media>): string {
   if (media.length === 0) {
     return 'corrupted media';
@@ -64,7 +54,6 @@
 
 export {
   maxDimensions,
-  shimUploadURI,
   contentStringForMediaArray,
   multimediaMessagePreview,
   isLocalUploadID,
diff --git a/lib/shared/messages/multimedia-message-spec.js b/lib/shared/messages/multimedia-message-spec.js
--- a/lib/shared/messages/multimedia-message-spec.js
+++ b/lib/shared/messages/multimedia-message-spec.js
@@ -12,10 +12,8 @@
 import {
   contentStringForMediaArray,
   multimediaMessagePreview,
-  shimUploadURI,
 } from '../../media/media-utils.js';
 import type { PlatformDetails } from '../../types/device-types.js';
-import type { Media, Video, Image } from '../../types/media-types.js';
 import {
   messageTypes,
   assertMessageType,
@@ -24,7 +22,6 @@
 import type {
   MessageInfo,
   RawMessageInfo,
-  RawMultimediaMessageInfo,
   ClientDBMessageInfo,
 } from '../../types/message-types.js';
 import type {
@@ -209,32 +206,22 @@
     platformDetails: ?PlatformDetails,
   ): RawMediaMessageInfo | RawImagesMessageInfo | RawUnsupportedMessageInfo {
     if (rawMessageInfo.type === messageTypes.IMAGES) {
-      const shimmedRawMessageInfo = shimMediaMessageInfo(
-        rawMessageInfo,
-        platformDetails,
-      );
-      return shimmedRawMessageInfo;
-    } else {
-      const shimmedRawMessageInfo = shimMediaMessageInfo(
-        rawMessageInfo,
-        platformDetails,
-      );
-      // TODO figure out first native codeVersion supporting video playback
-      if (hasMinCodeVersion(platformDetails, 158)) {
-        return shimmedRawMessageInfo;
-      }
-      const { id } = shimmedRawMessageInfo;
-      invariant(id !== null && id !== undefined, 'id should be set on server');
-      return {
-        type: messageTypes.UNSUPPORTED,
-        id,
-        threadID: shimmedRawMessageInfo.threadID,
-        creatorID: shimmedRawMessageInfo.creatorID,
-        time: shimmedRawMessageInfo.time,
-        robotext: multimediaMessagePreview(shimmedRawMessageInfo),
-        unsupportedMessageInfo: shimmedRawMessageInfo,
-      };
+      return rawMessageInfo;
     }
+    if (hasMinCodeVersion(platformDetails, 158)) {
+      return rawMessageInfo;
+    }
+    const { id } = rawMessageInfo;
+    invariant(id !== null && id !== undefined, 'id should be set on server');
+    return {
+      type: messageTypes.UNSUPPORTED,
+      id,
+      threadID: rawMessageInfo.threadID,
+      creatorID: rawMessageInfo.creatorID,
+      time: rawMessageInfo.time,
+      robotext: multimediaMessagePreview(rawMessageInfo),
+      unsupportedMessageInfo: rawMessageInfo,
+    };
   },
 
   unshimMessageInfo(
@@ -323,54 +310,6 @@
   includedInRepliesCount: true,
 });
 
-function shimMediaMessageInfo(
-  rawMessageInfo: RawMultimediaMessageInfo,
-  platformDetails: ?PlatformDetails,
-): RawMultimediaMessageInfo {
-  if (rawMessageInfo.type === messageTypes.IMAGES) {
-    let uriChanged = false;
-    const newMedia: Image[] = [];
-    for (const singleMedia of rawMessageInfo.media) {
-      const shimmedURI = shimUploadURI(singleMedia.uri, platformDetails);
-      if (shimmedURI === singleMedia.uri) {
-        newMedia.push(singleMedia);
-      } else {
-        newMedia.push(({ ...singleMedia, uri: shimmedURI }: Image));
-        uriChanged = true;
-      }
-    }
-    if (!uriChanged) {
-      return rawMessageInfo;
-    }
-    return ({
-      ...rawMessageInfo,
-      media: newMedia,
-    }: RawImagesMessageInfo);
-  } else {
-    let uriChanged = false;
-    const newMedia: Media[] = [];
-    for (const singleMedia of rawMessageInfo.media) {
-      const shimmedURI = shimUploadURI(singleMedia.uri, platformDetails);
-      if (shimmedURI === singleMedia.uri) {
-        newMedia.push(singleMedia);
-      } else if (singleMedia.type === 'photo') {
-        newMedia.push(({ ...singleMedia, uri: shimmedURI }: Image));
-        uriChanged = true;
-      } else {
-        newMedia.push(({ ...singleMedia, uri: shimmedURI }: Video));
-        uriChanged = true;
-      }
-    }
-    if (!uriChanged) {
-      return rawMessageInfo;
-    }
-    return ({
-      ...rawMessageInfo,
-      media: newMedia,
-    }: RawMediaMessageInfo);
-  }
-}
-
 // Four photos were uploaded before dimensions were calculated server-side,
 // and delivered to clients without dimensions in the MultimediaMessageInfo.
 const preDimensionUploads = {