diff --git a/tests/unit/test_filters.py b/tests/unit/test_filters.py
index b7c7a2ce04b9..e78ca40918f0 100644
--- a/tests/unit/test_filters.py
+++ b/tests/unit/test_filters.py
@@ -301,3 +301,36 @@ def test_remove_invalid_xml_unicode(inp, expected):
Test that invalid XML unicode characters are removed.
"""
assert filters.remove_invalid_xml_unicode(inp) == expected
+
+
+def test_show_share_image():
+ # Missing user-agent header
+ assert filters.show_share_image(None) is True
+ # Empty user-agent header
+ assert filters.show_share_image("") is True
+
+ # Twitter/X - shows image
+ # https://developer.x.com/en/docs/x-for-websites/cards/guides/troubleshooting-cards#validate_twitterbot
+ assert filters.show_share_image("Twitterbot/1.0") is True
+
+ # Facebook - shows image
+ # https://developers.facebook.com/docs/sharing/webmasters/web-crawlers
+ assert (
+ filters.show_share_image(
+ "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"
+ )
+ is True
+ )
+ assert filters.show_share_image("facebookexternalhit/1.1") is True
+ assert filters.show_share_image("facebookcatalog/1.0") is True
+
+ # LinkedIn - shows image (https://www.linkedin.com/robots.txt)
+ assert filters.show_share_image("LinkedInBot") is True
+
+ # Slack - don't show image (https://api.slack.com/robots)
+ assert (
+ filters.show_share_image(
+ "Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)"
+ )
+ is False
+ )
diff --git a/warehouse/config.py b/warehouse/config.py
index 1014a1ca6dbe..2e025e2b2350 100644
--- a/warehouse/config.py
+++ b/warehouse/config.py
@@ -683,6 +683,7 @@ def configure(settings=None):
filters.setdefault(
"remove_invalid_xml_unicode", "warehouse.filters:remove_invalid_xml_unicode"
)
+ filters.setdefault("show_share_image", "warehouse.filters:show_share_image")
# We also want to register some global functions for Jinja
jglobals = config.get_settings().setdefault("jinja2.globals", {})
diff --git a/warehouse/filters.py b/warehouse/filters.py
index 68364adfc6d3..43f2125b519e 100644
--- a/warehouse/filters.py
+++ b/warehouse/filters.py
@@ -201,3 +201,22 @@ def remove_invalid_xml_unicode(value: str | None) -> str | None:
def includeme(config):
config.add_request_method(_camo_url, name="camo_url")
+
+
+def show_share_image(user_agent: str | None) -> bool:
+ """
+ Whether the og:image meta-tag should be included based on the user-agent
+
+ Used to exclude the image from Slack link-expansion.
+
+ """
+
+ # User agent header not included or empty
+ if not user_agent:
+ return True
+
+ # Don't show the og:image for Slackbot link-expansion requests
+ if user_agent.strip().startswith("Slackbot-LinkExpanding"):
+ return False
+
+ return True
diff --git a/warehouse/templates/base.html b/warehouse/templates/base.html
index bdd7f2054c5e..11b371cc18c8 100644
--- a/warehouse/templates/base.html
+++ b/warehouse/templates/base.html
@@ -113,9 +113,13 @@
+ {% if request.user_agent | show_share_image %}
+ {% endif %}
+
+
{% block extra_meta %}{% endblock %}