diff --git a/warehouse/__init__.py b/warehouse/__init__.py
index 211efbd5805297e247efe0e81d658afe53e2ef12..e02dcc40cb7c0902631461d458d9ebc951a4e991 100644
--- a/warehouse/__init__.py
+++ b/warehouse/__init__.py
@@ -6,7 +6,7 @@ from flask import Flask, render_template, request, redirect, url_for, Response,
 from requests_oauthlib import OAuth2Session
 
 from .models import db, Item, Location, Photo
-from .utils import render_pdf, print_pdf, qrcode_svg, render_markdown
+from .utils import render_pdf, print_pdf, qrcode_svg, render_markdown, send_thumbnail
 
 app = Flask(__name__, instance_relative_config=True)
 app.config.from_pyfile('config.cfg')
@@ -183,6 +183,16 @@ def item_locate(item_id):
 def photo_show(photo_id):
 	return Photo.query.get_or_404(photo_id).send()
 
+@app.route('/photos/<int:photo_id>/small')
+def photo_small(photo_id):
+	photo = Photo.query.get_or_404(photo_id)
+	return send_thumbnail(photo.image, 130, 100)
+
+@app.route('/photos/<int:photo_id>/medium')
+def photo_medium(photo_id):
+	photo = Photo.query.get_or_404(photo_id)
+	return send_thumbnail(photo.image, 390, 300)
+
 @app.route('/C/<code>')
 @app.route('/c/<code>')
 def qrcode_url(code):
diff --git a/warehouse/models.py b/warehouse/models.py
index 866bacce02d920c1ea0df43fd792d73e73acafbb..5803213ca6a6a1afcf692a7602398fa8b7049b39 100644
--- a/warehouse/models.py
+++ b/warehouse/models.py
@@ -3,6 +3,7 @@ import os
 
 from flask import current_app, send_file
 from flask_sqlalchemy import SQLAlchemy
+from PIL import Image
 
 db = SQLAlchemy()
 
@@ -41,6 +42,11 @@ class Photo(db.Model):
 		path = os.path.join(current_app.config['UPLOAD_FOLDER'], self.path)
 		return send_file(path, mimetype=self.mimetype)
 
+	@property
+	def image(self):
+		path = os.path.join(current_app.config['UPLOAD_FOLDER'], self.path)
+		return Image.open(path, formats=['JPEG'])
+
 def token_typable():
 	alphabet = '23456789ABCDEFGHJKLMNPQRSTUVWX' # not '01OIYZ'
 	return ''.join([secrets.choice(alphabet) for _ in range(5)])
diff --git a/warehouse/templates/item/list.html b/warehouse/templates/item/list.html
index 2df46a6165f005435aeb34c88684f9c4fd99ff0d..985e5037035ad1ef9df8144d7f73d57af1d13cc7 100644
--- a/warehouse/templates/item/list.html
+++ b/warehouse/templates/item/list.html
@@ -24,7 +24,7 @@
 			<td style="width: 140px;">
 				{% if item.photos %}
 					<a href="{{ url_for('item_view', item_id=item.id) }}" style="width: 130px; height: 100px;" class="d-flex align-items-center justify-content-center border text-bg-light">
-						<img src="{{ url_for('photo_show', photo_id=(item.photos|first).id) }}" style="max-width: 100%; max-height: 100%;">
+						<img src="{{ url_for('photo_small', photo_id=(item.photos|first).id) }}" style="max-width: 100%; max-height: 100%;">
 					</a>
 				{% endif %}
 			</td>
diff --git a/warehouse/templates/item/view.html b/warehouse/templates/item/view.html
index ce0d961ea54bb57bb54f5b18c195d51d91b6d37e..7e6905dc975401069616559a267bddb516b5c3d1 100644
--- a/warehouse/templates/item/view.html
+++ b/warehouse/templates/item/view.html
@@ -39,7 +39,7 @@
 				{% for photo in item.photos %}
 				<div class="carousel-item {{ 'active' if loop.first }}" style="width: 100%; height: 100%;">
 					<a href="{{ url_for('photo_show', photo_id=photo.id) }}" style="width: 100%; height: 100%;" class="d-flex align-items-center justify-content-center">
-						<img src="{{ url_for('photo_show', photo_id=photo.id) }}" style="max-width: 100%; max-height: 100%;">
+						<img src="{{ url_for('photo_medium', photo_id=photo.id) }}" style="max-width: 100%; max-height: 100%;">
 					</a>
 				</div>
 				{% endfor %}
diff --git a/warehouse/utils/__init__.py b/warehouse/utils/__init__.py
index 1fbb582e821e73c0a3cd55d1da01b59e2d7cc151..676a459079afb6fda3d98133f2cb72790438a6ac 100644
--- a/warehouse/utils/__init__.py
+++ b/warehouse/utils/__init__.py
@@ -2,7 +2,8 @@ from .pdf import render_pdf
 from .ipp import print_pdf
 from .codes import qrcode_svg
 
-from flask import Markup
+import io
+from flask import Markup, send_file
 import markdown
 import bleach
 
@@ -16,3 +17,11 @@ def render_markdown(text, short=False):
 		result = result.split('<p>', 1)[-1]
 		result = result.split('</p>', 1)[0]
 	return Markup(result)
+
+def send_thumbnail(img, max_width, max_height):
+	scale = min(max_width/img.width, max_height/img.height)
+	img.thumbnail((int(img.width*scale), int(img.height*scale)))
+	buf = io.BytesIO()
+	img.save(buf, format='JPEG', quality=70)
+	buf.seek(0)
+	return send_file(buf, mimetype='image/jpeg')