diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd9ded8a5..4e02f1446 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@ v2.4.8 (FUTURE)
 ## Bug Fixes
 
 * [#2473](https://github.com/digitalocean/netbox/issues/2473) - Fix encoding of long (>127 character) secrets
+* [#2558](https://github.com/digitalocean/netbox/issues/2558) - Filter on all tags when multiple are passed
 * [#2575](https://github.com/digitalocean/netbox/issues/2575) - Correct model specified for rack roles table
 
 ---
diff --git a/netbox/circuits/filters.py b/netbox/circuits/filters.py
index 79efdc950..a159fad42 100644
--- a/netbox/circuits/filters.py
+++ b/netbox/circuits/filters.py
@@ -6,7 +6,7 @@ from django.db.models import Q
 from dcim.models import Site
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 
@@ -28,9 +28,7 @@ class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Site (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Provider
@@ -106,9 +104,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Site (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Circuit
diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py
index 689e88a5d..8b40ca7b7 100644
--- a/netbox/dcim/filters.py
+++ b/netbox/dcim/filters.py
@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
-from utilities.filters import NullableCharFieldFilter, NumericInFilter
+from utilities.filters import NullableCharFieldFilter, NumericInFilter, TagFilter
 from virtualization.models import Cluster
 from .constants import (
     DEVICE_STATUS_CHOICES, IFACE_FF_LAG, NONCONNECTABLE_IFACE_TYPES, SITE_STATUS_CHOICES, VIRTUAL_IFACE_TYPES,
@@ -83,9 +83,7 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Tenant (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Site
@@ -196,9 +194,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Role (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Rack
@@ -306,9 +302,7 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Manufacturer (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = DeviceType
@@ -530,9 +524,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
         queryset=VirtualChassis.objects.all(),
         label='Virtual chassis (ID)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Device
@@ -592,9 +584,7 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
         to_field_name='name',
         label='Device (name)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
 class ConsolePortFilter(DeviceComponentFilterSet):
@@ -653,9 +643,7 @@ class InterfaceFilter(django_filters.FilterSet):
         method='_mac_address',
         label='MAC address',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
     vlan_id = django_filters.CharFilter(
         method='filter_vlan_id',
         label='Assigned VLAN'
@@ -797,9 +785,7 @@ class VirtualChassisFilter(django_filters.FilterSet):
         to_field_name='slug',
         label='Tenant (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = VirtualChassis
diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py
index 0a8606e52..700a25ae9 100644
--- a/netbox/ipam/filters.py
+++ b/netbox/ipam/filters.py
@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 from dcim.models import Site, Device, Interface
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from virtualization.models import VirtualMachine
 from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
@@ -31,9 +31,7 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Tenant (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     def search(self, queryset, name, value):
         if not value.strip():
@@ -73,9 +71,7 @@ class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='RIR (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Aggregate
@@ -174,9 +170,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=PREFIX_STATUS_CHOICES,
         null_value=None
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Prefix
@@ -303,9 +297,7 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
     role = django_filters.MultipleChoiceFilter(
         choices=IPADDRESS_ROLE_CHOICES
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = IPAddress
@@ -422,9 +414,7 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=VLAN_STATUS_CHOICES,
         null_value=None
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = VLAN
@@ -466,9 +456,7 @@ class ServiceFilter(django_filters.FilterSet):
         to_field_name='name',
         label='Virtual machine (name)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Service
diff --git a/netbox/secrets/filters.py b/netbox/secrets/filters.py
index f43a82b22..aa7e02e43 100644
--- a/netbox/secrets/filters.py
+++ b/netbox/secrets/filters.py
@@ -5,7 +5,7 @@ from django.db.models import Q
 
 from dcim.models import Device
 from extras.filters import CustomFieldFilterSet
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .models import Secret, SecretRole
 
 
@@ -42,9 +42,7 @@ class SecretFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='name',
         label='Device (name)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Secret
diff --git a/netbox/tenancy/filters.py b/netbox/tenancy/filters.py
index 7eccff5d3..4ff620d39 100644
--- a/netbox/tenancy/filters.py
+++ b/netbox/tenancy/filters.py
@@ -4,7 +4,7 @@ import django_filters
 from django.db.models import Q
 
 from extras.filters import CustomFieldFilterSet
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .models import Tenant, TenantGroup
 
 
@@ -31,9 +31,7 @@ class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         label='Group (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Tenant
diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py
index 90cdcd9fc..a7f23d2f6 100644
--- a/netbox/utilities/filters.py
+++ b/netbox/utilities/filters.py
@@ -5,6 +5,7 @@ import itertools
 import django_filters
 from django import forms
 from django.utils.encoding import force_text
+from taggit.models import Tag
 
 
 #
@@ -68,3 +69,18 @@ class NullableModelMultipleChoiceField(forms.ModelMultipleChoiceField):
             stripped_value = value
         super(NullableModelMultipleChoiceField, self).clean(stripped_value)
         return value
+
+
+class TagFilter(django_filters.ModelMultipleChoiceFilter):
+    """
+    Match on one or more assigned tags. If multiple tags are specified (e.g. ?tag=foo&tag=bar), the queryset is filtered
+    to objects matching all tags.
+    """
+    def __init__(self, *args, **kwargs):
+
+        kwargs.setdefault('name', 'tags__slug')
+        kwargs.setdefault('to_field_name', 'slug')
+        kwargs.setdefault('conjoined', True)
+        kwargs.setdefault('queryset', Tag.objects.all())
+
+        super(TagFilter, self).__init__(*args, **kwargs)
diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py
index 99df19aee..5f0f834cc 100644
--- a/netbox/virtualization/filters.py
+++ b/netbox/virtualization/filters.py
@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .constants import VM_STATUS_CHOICES
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
@@ -64,9 +64,7 @@ class ClusterFilter(CustomFieldFilterSet):
         to_field_name='slug',
         label='Site (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = Cluster
@@ -168,9 +166,7 @@ class VirtualMachineFilter(CustomFieldFilterSet):
         to_field_name='slug',
         label='Platform (slug)',
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
     class Meta:
         model = VirtualMachine