diff --git a/include/staff/templates/thread-entries.tmpl.php b/include/staff/templates/thread-entries.tmpl.php
index 7e5e1638824a5491a1ec11d1cdf8b51299b853c0..a3f3bdfc725a80b0c636933286c3b50b63d03e01 100644
--- a/include/staff/templates/thread-entries.tmpl.php
+++ b/include/staff/templates/thread-entries.tmpl.php
@@ -4,6 +4,13 @@ $events = $events->getIterator();
 $events->rewind();
 $event = $events->current();
 $htmlId = $options['html-id'] ?: ('thread-'.$this->getId());
+
+$thread_attachments = array();
+foreach (Attachment::objects()->filter(array(
+    'thread_entry__thread__id' => $this->getId(),
+))->select_related('thread_entry', 'file') as $att) {
+    $thread_attachments[$att->object_id][] = $att;
+}
 ?>
 <div id="<?php echo $htmlId; ?>">
     <div id="thread-items" data-thread-id="<?php echo $this->getId(); ?>">
@@ -58,16 +65,16 @@ $htmlId = $options['html-id'] ?: ('thread-'.$this->getId());
         // Set inline image urls.
         <?php
         $urls = array();
-        foreach (AttachmentFile::objects()->filter(array(
-            'attachments__thread_entry__thread__id' => $this->getId(),
-            'attachments__inline' => true,
-        )) as $file) {
-            $urls[strtolower($file->getKey())] = array(
-                'download_url' => $file->getDownloadUrl(),
-                'filename' => $file->name,
+        foreach ($thread_attachments as $eid=>$atts) {
+            foreach ($atts as $A) {
+                if (!$A->inline)
+                    continue;
+                $urls[strtolower($A->file->getKey())] = array(
+                    'download_url' => $A->file->getDownloadUrl(),
+                    'filename' => $A->getFilename(),
                 );
-
             }
+        }
         ?>
         $('#'+container).data('imageUrls', <?php echo JsonDataEncoder::encode($urls); ?>);
         // Trigger thread processing.
diff --git a/include/staff/templates/thread-entry.tmpl.php b/include/staff/templates/thread-entry.tmpl.php
index 3f3bfee7c97316a62a87ac00716eafdc7e513a32..4a86b68ee0f17bffd1c9647c8fca4bae081a2731 100644
--- a/include/staff/templates/thread-entry.tmpl.php
+++ b/include/staff/templates/thread-entry.tmpl.php
@@ -70,10 +70,11 @@ if ($user)
     // The strangeness here is because .has_attachments is an annotation from
     // Thread::getEntries(); however, this template may be used in other
     // places such as from thread entry editing
-    if (isset($entry->has_attachments) ? $entry->has_attachments
-            : $entry->attachments->filter(array('inline'=>0))->count()) { ?>
+    $atts = isset($thread_attachments) ? $thread_attachments[$entry->id] : $entry->attachments;
+    if (isset($atts) && $atts) {
+?>
     <div class="attachments"><?php
-        foreach ($entry->attachments as $A) {
+        foreach ($atts as $A) {
             if ($A->inline)
                 continue;
             $size = '';
@@ -87,12 +88,13 @@ if ($user)
             target="_blank"><?php echo Format::htmlchars($A->getFilename());
         ?></a><?php echo $size;?>
         </span>
-<?php   }  ?>
-    </div>
-<?php } ?>
+<?php   }
+    echo '</div>';
+    }
+?>
     </div>
 <?php
-    if ($urls = $entry->getAttachmentUrls()) { ?>
+    if (!isset($thread_attachments) && ($urls = $entry->getAttachmentUrls())) { ?>
         <script type="text/javascript">
             $('#thread-entry-<?php echo $entry->getId(); ?>')
                 .data('urls', <?php
diff --git a/scp/css/scp.css b/scp/css/scp.css
index a933d2855f952b678ccf3fbbc3e9b6d3da7c3947..4556855d36102a2e1085a66dcfdafae46cf568fd 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -1047,6 +1047,9 @@ img.avatar {
   border-top-color: rgba(0,0,0,0.2);
   border-radius: 0 0 6px 6px;
 }
+.thread-body .attachments:empty {
+    display: none;
+}
 .thread-body .attachments .filesize {
   margin-left: 0.5em;
   line-height: 1em;