diff --git a/sites/all/modules/ad/channel/ad_channel.inc b/sites/all/modules/ad/channel/ad_channel.inc
index d33c66f..5859495 100644
--- a/sites/all/modules/ad/channel/ad_channel.inc
+++ b/sites/all/modules/ad/channel/ad_channel.inc
@@ -23,11 +23,42 @@ function ad_channel_cache_filter($ads) {
   $valid_ads = array();
   $nochannel_fallback_ads = array();
   $nochannel_percent = array();
+
+  // preparing node-level data for taxonomy filter
+  // TODO: not exactly too inefficient; should re-implement w/ no-DB caching
+  $node_id = adserve_variable('url');
+  $is_node = strpos($node_id, 'node/');
+  if ($is_node !== false) {
+    $node = new stdClass;
+    $node_id = substr($node_id, 5);
+    $node->vid = $node_id;
+    // alternative: use in-loop call to taxonomy_node_get_terms_by_vocabulary($node, $vid, $key = 'tid')
+    $terms = taxonomy_node_get_terms($node);
+    $allowed_vids = variable_get('ad_channel_allowed_vids', false);
+    #echo "allowed vocabulary ids:"; var_dump($allowed_vids);
+    #echo "node 'object': "; var_dump($node);
+    #echo "node terms: "; var_dump($terms);
+    // clean node's $terms from non-matching vocabularies, flatten $terms
+    $terms_new = array();
+    if ($allowed_vids !== false) {
+      foreach ($terms as $term_id => $tmp_obj) {
+        if (in_array($tmp_obj->vid, $allowed_vids)) {
+          $terms_new[$tmp_obj->vid] = $term_id;
+        }
+      }
+    }
+    $terms = $terms_new;
+    unset($terms_new);
+    unset($tmp_obj);
+    unset($term_id);
+    //echo "terms after cleaning/flattening: "; print_r($terms);
+  }
+
   // determine which channels each advertisement is assigned to
   foreach ($ads as $aid) {
     _debug_echo("ad_channel_cache: checking aid($aid)");
-    if (is_array($channels['ads']) && isset($channels['ads'][$aid]) &&
-        is_array($channels['ads'][$aid])) {
+    if (is_array($channels['ads']) && isset($channels['ads'][$aid]) && is_array($channels['ads'][$aid])) {
+      reset($channels['ads'][$aid]);
       foreach ($channels['ads'][$aid] as $chid) {
         $channel = $channels['channels'][$chid];
         $display_by_url = $channel->display;
@@ -69,6 +100,47 @@ function ad_channel_cache_filter($ads) {
         // no match so we didn't hit a break, ad is not valid
         _debug_echo("ad_channel_cache: aid($aid) is not valid");
       }
+
+      // ads by taxonomy filter
+
+      // skip taxonomy filter if: $match is true, this ad is already valid,
+      // or if not on a node page (taxonomy makes sense only for nodes)
+      if ($match || $is_node === false || (is_array($valid_ads) && in_array($aid, $valid_ads[$chid]))) {
+        continue;
+      }
+      reset($channels['ads'][$aid]);
+      foreach ($channels['ads'][$aid] as $chid) {
+        $channel = $channels['channels'][$chid];
+        $channel_terms = variable_get("ad_channel_allowed_terms_$chid", 0);
+        $match = false;
+        if (is_array($channel_terms)) {
+          foreach ($terms as $vid => $term_id) {
+            foreach ($channel_terms as $vid_ch => $term_ch) {
+              if ($vid == $vid_ch && array_key_exists($term_id, $term_ch)) {
+                $match = true;
+                break 2;
+              }
+            }
+          }
+        }
+        _debug_echo("ad_channel_cache: checking aid($aid) against channel($chid) url (" . adserve_variable('url') . ") terms (" . serialize($channel_terms) . ") node terms (" . serialize($terms) . ") match($match)");
+        //echo "ad_channel_cache: checking aid($aid) against channel($chid) url (" . adserve_variable('url') . ") terms (" . serialize($channel_terms) . ") node terms (" . serialize($terms) . ") match($match)\n";
+        // display ad on matching taxonomy terms
+        if ($match) {
+          _debug_echo("ad_channel_cache: aid($aid) is valid (taxonomy term matched)");
+          $valid_ads[$chid][] = $aid;
+          if ($nochannel_display == 1) {
+            $nochannel_percent[$chid] = $channel->no_channel_percent;
+            _debug_echo("ad_channel_cache: channel($chid) no_channel_percent($nochannel_percent[$chid])");
+          }
+          else {
+            _debug_echo("ad_channel_cache: channel($chid)");
+          }
+          break;
+        }
+        // no match so we didn't hit a break, ad is not valid
+        _debug_echo("ad_channel_cache: aid($aid) is not valid (taxonomy terms didn't match)");
+      }
     }
     else {
       // no channel information for ad
